Невозможно привязать lvalue к A ‹Cv2› &&

Я думал, что универсальная ссылка (T&&) должна принимать любые ссылки. Но следующее не работает.

Я сталкиваюсь с этой проблемой, когда пытаюсь быть константно-правильным в библиотеке, которую я пишу. Я новичок в C ++ и раньше не видел ничего подобного.

test.cpp:

enum Cv_qualifier {
    constant,
    non_const
};
template <Cv_qualifier Cv> class A;
template<>
class A<Cv_qualifier::constant> {
public:
    template<Cv_qualifier Cv2> 
    void t(const A<Cv2>&& out) {}
};

template <>
class A<Cv_qualifier::non_const> {
public:
    template<Cv_qualifier Cv2> 
    void t(const A<Cv2>&& out) {}
};

int main()
{
    A<Cv_qualifier::non_const> a;
    A<Cv_qualifier::constant> b;
    a.t(b);
}

Ошибка (скомпилировано с g++ test.cpp -std=c++11):

test.cpp: In function ‘int main()’:
test.cpp:24:10: error: cannot bind ‘A<(Cv_qualifier)0u>’ lvalue to ‘const A<(Cv_qualifier)0u>&&’
     a.t(b);
          ^
test.cpp:17:10: note:   initializing argument 1 of ‘void A<(Cv_qualifier)1u>::t(const A<Cv2>&&) [with Cv_qualifier Cv2 = (Cv_qualifier)0u]’
     void t(const A<Cv2>&& out) {}
          ^

Между прочим, в реальной программе class A не владеет никакими фактическими данными и содержит ссылки на другой класс, который фактически хранит данные. Надеюсь, это означает, что я не создаю постоянно косвенные / копируемые данные, когда разрешаю функции-члену t из class A принимать временные объекты.


person hamster on wheels    schedule 15.11.2016    source источник
comment
Вы не используете универсальные ссылки. && - это не универсальная ссылка, это ссылка на rvalue. T && - это когда T является параметром шаблона, выводимым типом, с которым он вызывается. Другими словами, T должен быть параметром шаблона, а параметр функции AND T должен быть выведен, а не указан.   -  person Edward Strange    schedule 15.11.2016
comment
Так что-то вроде std::vector<T>&& или class_name<Template parameters>&& не являются универсальными ссылками? А единственный способ написать универсальную ссылку - это T&&? Я думал, что нужно вывести параметр шаблона Cv_2 функции-члена t.   -  person hamster on wheels    schedule 15.11.2016
comment
Если мне нужен аргумент, который может принимать как l-значение, так и ссылку r-значение на конкретный класс вместо видов классов. Это возможно?   -  person hamster on wheels    schedule 16.11.2016
comment
@rxu Вы можете использовать SFINAE, чтобы ограничить вывод T.   -  person NathanOliver    schedule 16.11.2016
comment
Большое спасибо. Я целый день пытался в этом разобраться. Таким образом, T&& - это единственный способ создать аргумент, который принимает ссылки как на l-значение, так и на r-значение.   -  person hamster on wheels    schedule 16.11.2016
comment
То есть что-то вроде std :: vector ‹T› && или class_name ‹Параметры шаблона› && не являются универсальными ссылками? Вы правы. Универсальная ссылка возникает только тогда, когда компилятор может выполнять свертывание ссылок.   -  person Guillaume Racicot    schedule 16.11.2016
comment
Нет, T&& - не единственный способ для функции принимать l-значения и r-значения. В вашем случае все, что вам нужно сделать, это принять A<Cv2> const&, и он примет все, что угодно.   -  person Edward Strange    schedule 16.11.2016
comment
универсальная ссылка возникает только тогда, когда параметр равен T&&, а T является параметром шаблона функции   -  person M.M    schedule 16.11.2016
comment
@ Сумасшедший Эдди: может ли параметр A<Cv2> const& a предотвратить изменение значений a? Хорошо, я понимаю. Таким образом, он ограничивает изменение значений a. это постоянная ссылка. stackoverflow.com/ questions / 3694630 /   -  person hamster on wheels    schedule 16.11.2016


Ответы (1)


Универсальная ссылка, или ссылка пересылки, возникает только из-за сворачивания ссылки. Это работает так:

T&& & -> T&
T& && -> T&
T&& && -> T&&

Таким образом, когда вы получаете T&& в функции шаблона, ссылка rvalue может свернуться к другим типам ссылок в зависимости от типа T. В любых других случаях, когда сворачивания не происходит, SomeType&& останется SomeType&& и будет ссылкой на rvalue.

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

template <Cv_qualifier Cv> struct A;

template<>
struct A<Cv_qualifier::constant> {
    template<typename T> 
    void t(T&& out) {}
};

template <>
struct A<Cv_qualifier::non_const> {
    template<typename T> 
    void t(T&& out) {}
};

Действительно, сейчас происходит обрушение. Если вы хотите извлечь значение Cv_qualifier из T, вы можете сделать себе типаж, который это сделает:

template<typename>
struct CvValue;

template<Cv_qualifier cv>
struct CvValue<A<cv>> {
    constexpr static Cv_qualifier value = cv;
};

Затем внутри вашей функции t вы можете сделать это:

//                   v----- This is a good practice to apply a constraint
template<typename T, std::void_t<decltype(CvValue<std::decay_t<T>>::value)>* = 0> 
auto t(T&& out) {
    constexpr auto cv = CvValue<std::decay_t<T>>::value;

    // do whatever you want with cv
}

Если вы не можете использовать std::void_t из C ++ 17, вы можете реализовать его так:

template<typename...>
using void_t = void;

Однако, если вы хотите только проверить, является ли T A<...>, используйте это:

template<typename>
struct is_A : std::false_type {};

template<Cv_qualifier cv>
struct is_A<A<cv>> : std::true_type {};

Не забудьте использовать его с std::decay_t:

template<typename T, std::enable_if_t<std::is_A<std::decay_t<T>>::value>* = 0> 
void t(T&& out) {}
person Guillaume Racicot    schedule 15.11.2016
comment
Большое спасибо за ответ! Это действительно делает универсальный справочник более понятным. На данный момент я пытаюсь использовать общедоступный typedef в классе A, чтобы A::Cv_ сообщал параметр шаблона Cv или A. Я также пытаюсь написать черту is_A, чтобы проверить, является ли typename class A. - person hamster on wheels; 16.11.2016
comment
Чтобы сообщить о параметре шаблона в A, вы должны использовать не определение типа, а константу, объявленную как таковую: constexpr static Cv_qualifier = Cv;, как и мой CvValue. Я обновлю свой ответ, чтобы реализовать черту is_A. - person Guillaume Racicot; 16.11.2016
comment
Кажется, мне тоже нужен struct is_A<A<cv>& > : std::true_type {};. Теперь работает :) - person hamster on wheels; 16.11.2016
comment
Это потому, что вы пропустили std::decay_t. T может разрешать различные типы ссылок, но характеристика типа не знает ссылок. Вам нужно использовать is_A<std::decay_t<T>>::value - person Guillaume Racicot; 16.11.2016