Как я могу использовать черты типа, чтобы ограничить универсальные ссылочные параметры функции ссылками на r-значения? - PullRequest
0 голосов
/ 29 августа 2018

Я хочу определить функцию op2() с универсальным справочным параметром.

В зависимости от типа, я хочу различить далее, если параметр функции в op2() является ссылкой на r-значение типа test. Если это так, я хочу определить тип возвращаемого значения op2 как int. Во всех остальных случаях тип возвращаемого значения должен быть void.

Вот мое Я:

#include <type_traits>
#include <iostream>
#include <string>

class test {
    public:
    test(const std::string& str) : _str(str){}
    test(test&& t) = default;
    test(const test& t) = default;

    friend std::ostream& operator<<(std::ostream& os, test&& t) {
        os << t._str;
        return os;
    }

    private:
    std::string _str;
};

template<typename T>
void op(T&& x) {
    std::cout << " is rvalue ref? " << std::is_rvalue_reference<decltype(x)>::value << std::endl;
    std::cout << " is a test rvalue ref? " << std::is_same<test&&, decltype(x)>::value << std::endl;
    std::cout << std::forward<T>(x) << std::endl;
}

template<typename T>        // This is A
typename std::enable_if<std::negation<std::conjunction<std::is_same<test, T>, std::is_rvalue_reference<T>>>::value>::type op2(T&& x) {
    std::cout << "op2: A: " << std::forward<T>(x) << std::endl;
}

template<typename T>    // This is B
typename std::enable_if<std::conjunction<std::is_same<test, T>, std::is_rvalue_reference<T>>::value, int>::type op2(T&& x) {
    std::cout << "op2: B: " << std::move(x) << std::endl;
    return EXIT_SUCCESS;
}

int main() {
    op(std::string{"r-value string"});
    std::string str{"l-value string"};
    op(str);
    op(test{"test"});

    op2(std::string{"r-value string"}); //gets into A
    op2(str); //gets into A
    op2(test{"r-value string"}); //Should get into B, but actually gets into A
}

Проблема с кодом заключается в том, что последний op2() -колл переходит в неправильную перегрузку.

Я пробовал другую функцию, op(), которая получает правильные типы с помощью decltype(), но я не знаю, как я могу использовать decltype() в признаке типа.

Я использую C ++ 17 с gcc8.2

Ответы [ 3 ]

0 голосов
/ 29 августа 2018

Ссылка для пересылки T&& никогда не выведет аргумент шаблона T как тип ссылки rvalue. Если аргумент является значением типа test, то T будет выводиться как test, тогда как если аргумент является значением l * типа 1006 *, то T будет выводиться как test&.

Поэтому перегрузки следует переписать как:

template<typename T>
std::enable_if_t<!std::is_same_v<test, T>, void> op2(T&& x) {
    std::cout << "op2: A: " << std::forward<T>(x) << std::endl;
}

template<typename T>
std::enable_if_t<std::is_same_v<test, T>, int> op2(T&& x) {
    std::cout << "op2: B: " << std::move(x) << std::endl;
    return EXIT_SUCCESS;
}
0 голосов
/ 29 августа 2018

В вашем случае простая перегрузка проще:

template<typename T>
void op2(T&& x) { // This is A
    std::cout << "op2: A: " << std::forward<T>(x) << std::endl;
}

int op2(test&& x) { // This is B
    std::cout << "op2: B: " << std::move(x) << std::endl;
    return EXIT_SUCCESS;
}

Демо

0 голосов
/ 29 августа 2018

Чтобы использовать decltype, когда у вас есть только типы, вы должны использовать std::declval:

decltype(std::declval<T>())

Но вы всегда получите реальную ссылку, так что это не то, что вы хотите. Вместо этого используйте тип, который выводится ссылкой на пересылку. T не будет ссылкой, если вы передадите rvalue, и будет T& для lvalue.

Это означает, что вы можете изменить B на:

template<typename T>
std::enable_if_t<std::is_same_v<test, T>, int> op2(T&& x);

Потому что, как было сказано ранее, если вы передадите значение r типа test, T будет выведено в test.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...