2016-03-31 10 views
8
#include<iostream> 
#include<thread> 
using namespace std; 

void f1(double& ret) { 
    ret=5.; 
} 

int main() { 
    double ret=0.; 
    thread t1(f1, ret); 
    t1.join(); 
    cout << "ret=" << ret << endl; 
} 

Приведенный выше код не компиляции со следующим error message:C++ Автор принимает аргумент ссылки не удалось собрать

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out 
In file included from /usr/local/include/c++/5.3.0/thread:39:0, 
       from main.cpp:2: 
/usr/local/include/c++/5.3.0/functional: In instantiation of 'struct std::_Bind_simple<void (*(double))(double&)>': 
/usr/local/include/c++/5.3.0/thread:137:59: required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(double&); _Args = {double&}]' 
main.cpp:11:21: required from here 
/usr/local/include/c++/5.3.0/functional:1505:61: error: no type named 'type' in 'class std::result_of<void (*(double))(double&)>' 
     typedef typename result_of<_Callable(_Args...)>::type result_type; 
                  ^
/usr/local/include/c++/5.3.0/functional:1526:9: error: no type named 'type' in 'class std::result_of<void (*(double))(double&)>' 
     _M_invoke(_Index_tuple<_Indices...>) 
     ^

Я понимаю, что я могу использовать std::ref() передать аргумент. Но если я передаю по значению, почему это ошибка, так как thread должен просто скопировать аргумент значение и передать некоторый объект, хранящийся внутри потока, для связывания с эталонным аргументом функции f1.

Я чувствую, что если я могу понять, что делает этот result_of и почему он дает ошибку, я могу лучше понять причину. Так может ли кто-нибудь пройти меня через сообщение об ошибке? В частности, значения std::_Bind_simple<void (*(double))(double&)> и std::result_of<void (*(double))(double&)>.

EDIT: Я знаю, что если передаю значение, поток будет работать только с копией и не будет иметь эффекта после возврата потока. Это не мое беспокойство. Я хочу знать, почему он дает ошибку сейчас, но это не дает ошибку на другие должности на SO, как следующее: Difference between pointer and reference as thread parameter

+0

@Barry, вы потеряли «выделенную часть» с редактирования. –

+0

@JonathanWakely Исправлено. – Barry

ответ

12

Я знаю, что если я передать значение, поток будет работать только на копии и не влияет на результат.

Нет, это не так. Код не должен делать копии и работать над копией, стандарт говорит, что он даже не компилируется.

Стандарт требует, чтобы аргументы вызываемой функции копировались (в хранилище, управляемом средой C++), а затем копии пересылаются как rvalues ​​. Поэтому в вашем примере f1 получает значение rvalue типа double, а параметр типа double& не может связываться с этим значением rvalue.

Причина, по которой стандарт требует, чтобы не было тихого копирования и потери данных: если функция требует модифицируемой ссылки, она не будет компилироваться, если вы не передадите ссылку явно, используя reference_wrapper.

Ошибка компилятора, которую вы получаете, включает в себя result_of, потому что именно так я сделал GCC std::thread, чтобы проверить, можно ли вызвать функцию с предоставленными аргументами. Я использую result_of<decltype(&f1)(double)>, чтобы проверить, можно ли вызвать указатель функции &f1 (который имеет тип void(*)(double&)) с rvalue типа double. Это не может быть вызвана с аргументом такого типа, поэтому вложенный тип result_of<decltype(&f1)(double)>::type не определен, поэтому компилятор говорит:

error: no type named 'type' in 'class std::result_of<void (*(double))(double&)>' 

Ошибка немного сбивает с толку, потому что правила описатель C++ означает, что decltype(&f1)(double) получает отображается как void(*(double))(double&).

Это не мое беспокойство. Я хочу знать, почему это даёт ошибку сейчас, но не дает ошибок другим сообщениям на SO

Эти сообщения использовали старый pre-C++ 11 или несоответствующий компилятор, который не встречал требования стандарта C++ 11 и некорректно скомпилированный код.

+0

от http://en.cppreference.com/w/cpp/thread/thread/thread, он ясно говорит, что он скопирует/переместит аргументы в ** доступную для потолка память ** – Rich

+1

И что? Это не значит, что он должен компилироваться. Удостоверьтесь, что вы обновили мой ответ, чтобы увидеть последнюю версию, в которой объясняется, что копии пересылаются как rvalues, поэтому не могут связываться с неконстантными ссылочными параметрами lvalue. –

+0

Можете ли вы объяснить ошибку 'std :: result_of'? Это попытка сделать то, что требует стандарт, как вы сказали, т.е. передать копии как rvalues? – Rich

1

Ответ Джонатана окончательный. Время, потраченное на изучение этого, было бы потрачено достаточно времени.

В то же время, изменив код таким образом, будет делать то, что вы хотите, а именно - отправить ссылку в функции потока:

thread t1(f1, std::ref(ret));