2016-10-24 1 views
2
#include <iostream> 


template <typename T> 
struct Wrapper { 
    operator T const &() const & { 
     std::cout << "Wrapper::operator T const &() const &\n"; 
     return _obj; 
    } 

    operator T&() & { 
     std::cout << "Wrapper::operator T&() &\n"; 
     return _obj; 
    } 

    operator T&&() && { 
     std::cout << "Wrapper::operator T&&() &&\n"; 
     return std::move(_obj); 
    } 

private: 
    T _obj; 
}; 

struct Test { 
    Test& operator=(Test const &test) { 
     std::cout << "Test& Test::operator=(Test const &)\n"; 
     return *this; 
    } 

    Test& operator=(Test &&test) { 
     std::cout << "Test& Test::operator=(Test &&)\n"; 
     return *this; 
    } 
}; 

int main() { 
    Test test; 
    Wrapper<Test> wrapperTest; 

    test = wrapperTest;    // OK for all 
    test = std::move(wrapperTest); // OK for GCC and ICC, not for Clang and VC++ 

    return 0; 
} 

VC++:Перегруженная двумерность оператора на Clang, но не на GCC, какая из них правильная?

(34): error C2593: 'operator =' is ambiguous

(26): note: could be 'Test &Test::operator =(Test &&)'

(25): note: or 'Test &Test::operator =(const Test &)'

(69): note: while trying to match the argument list '(Test, Wrapper)'

========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Clang:

:34:7: error: use of overloaded operator '=' is ambiguous (with operand types 'Test' and 'typename std::remove_reference &>::type' (aka 'Wrapper'))

test = std::move(wrapperTest); // OK for GCC and ICC, not for Clang and Microsoft Visual C++

~~~^~~~~~~~~~~~~~~~~~~~~~~

:25:8: note: candidate function

Test& operator=(Test const &test) { std::cout << "Test& Test::operator=(Test const &)\n"; return *this; }

^

:26:8: note: candidate function

Test& operator=(Test &&test) { std::cout << "Test& Test::operator=(Test &&)\n"; return *this; }

^

1 error generated.

+0

Где ваш пример для '' г ++? Вы предоставили для 'VC++', но название вашего вопроса говорит 'g ++'. Что он? Visual Studio C++ - это другой компилятор, чем GNU g ++. –

+0

Я в замешательстве. Они оба упоминают одну и ту же проблему: 'operator =()' неоднозначно. Как все иначе? –

+0

@ThomasMatthews Я сказал, что этот код компилируется на GCC, но не на Clang и VC++, я помещаю сообщение об ошибке Clang и VC++. – Johnmph

ответ

3

Я думаю, что НКУ и КМК являются правильными.

test = std::move(wrapperTest); 

назначает Wrapper<Test>&& к Test, так как ни один из кандидатов не соответствует для этого вызова, он будет рассматривать операцию, которая занимает не более 1 преобразования.

От Value categories:

When used as a function argument and when two overloads of the function are available, one taking rvalue reference parameter and the other taking lvalue reference to const parameter, an rvalue binds to the rvalue reference overload (thus, if both copy and move constructors are available, an rvalue argument invokes the move constructor, and likewise with copy and move assignment operators).

И Non-static member functions:

A non-static member function can be declared with either an lvalue ref-qualifier (the token & after the function name) or rvalue ref-qualifier (the token && after the function name). During overload resolution, non-static cv-qualified member function of class X is treated as a function that takes an implicit parameter of type lvalue reference to cv-qualified X if it has no ref-qualifiers or if it has the lvalue ref-qualifier. Otherwise (if it has rvalue ref-qualifier), it is treated as a function taking an implicit parameter of type rvalue reference to cv-qualified X.

Теперь, у нас есть эти кандидатуры:

  • Test& operator=(Test &&test) от operator T&&() &&
  • Test& operator=(Test const &test) по operator T const &() const &

Основываясь на этих пунктах, составители должны выбрать Test& operator=(Test &&test) по operator T&&() &&

+0

Возможно, этот ответ должен ссылаться на страницу [Перегрузка] (http://en.cppreference.com/w/cpp/language/overload_resolution)? –

+1

Почему компилятор должен выбрать '&&' перегрузку? Две неявные последовательности преобразования - это неотличимые пользовательские последовательности преобразования, поскольку они не содержат одну и ту же пользовательскую функцию преобразования ([\ [over.ics.rank \] /3.3] (http://eel.is/C++ проект/over.ics.rank # 3)). Насколько я могу судить, звонок действительно двусмыслен. – bogdan

+0

@bogdan Но поскольку std :: move (wrapperTest) является rvalue и из-за ref refifier на операторе преобразования, он должен выбрать rvalue версию, нет? – Johnmph