c ++ Используйте std :: enable_if для условного добавления получателей в шаблон варианта variadi c - PullRequest
1 голос
/ 10 апреля 2020

Я пытаюсь добавить специализации для случая, когда мой вариант имеет любой из int, float, bool и другие в качестве аргументов шаблона.

Моя попытка до сих пор :

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

template<typename... Types>
struct variant : std::variant<Types...> {

    using std::variant<Types...>::variant;

    template<typename T>
    const T& get() const { return std::get<T>(*this); }

#define VARGET(X) typename std::enable_if<(std::is_same<Types, X>::value ||  ... ), X>::type get_##X() const { return get<X>(); }

    VARGET(int)
    VARGET(float)
    VARGET(double)
    VARGET(bool)
    using std_string = std::string;
    VARGET(std_string)
};

int main()
{

    variant<int, float, bool> var = 3;
    std::cout << var.get_int() << std::endl;

    variant<int, float, bool> var2 = 0.1f;
    std::cout << var2.get_float() << std::endl;

    return 0;
}

Но на g cc 9 это выдает ошибку:

error: failed requirement 'std::is_same<int, double>::value || std::is_same<float, double>::value || std::is_same<bool, double>::value'; 'enable_if' cannot be used to disable this declaration

Почему это меня смущает :

Из моего понимания SFINAE, когда один из аргументов является одним из указанных типов, шаблон макроса оценивается следующим образом: (для примера int)

std::enable_if<(std::is_same<Types, int>::value || ... ), int>::type оценивается как int

таким образом выражение становится int get_int() const { std::get<int>(*this) }

И если оно не относится к одному из указанных типов:

std::enable_if<(std::is_same<Types, size_t>::value || ... ), size_t>::type вычисляется в ничто

, поэтому выражение становится get_size_t() const { std::get<size_t>(*this) }

Это недопустимый синтаксис, потому что функция не имеет возвращаемого типа, но из-за SFINAE она все равно должна компилироваться из-за других подстановок X, создающих допустимый синтаксис.

Что не так с мой код, и есть ли способ получить желаемый результат?

Спасибо.

Ответы [ 2 ]

1 голос
/ 10 апреля 2020

Я разобрался с ответом.

Функция SFINAE должна быть шаблоном с аргументом шаблона по умолчанию. то есть:

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

template<typename... Types>
struct variant : std::variant<Types...> {

    using std::variant<Types...>::variant;

    template<typename T>
    const T& get() const { return std::get<T>(*this); }

#define VARGET(X) template<typename T = X> std::enable_if_t<(std::is_same_v<Types, T> ||  ... ), X> get_##X() const { return get<X>(); }

    VARGET(int)
    VARGET(float)
    VARGET(double)
    VARGET(bool)
    using std_string = std::string;
    VARGET(std_string)
};

int main()
{

    variant<int, float, bool> var = 4;
    std::cout << var.get_int() << std::endl;
    //std::cout << var.get_std_string() << std::endl; compile error

    variant<int, std::string> var2 = "hello";
    std::cout << var2.get_std_string() << std::endl;

    return 0;
}
1 голос
/ 10 апреля 2020

Ошибка, которую я получаю при попытке скомпилировать ваш код, отличается от вашей, см. Compiler Explorer ; Я получаю ошибку «no type named type in ...», которую я ожидал, поскольку SFINAE не применяется в этом случае.

SFINAE будет применяться, например, при вызове функции, если есть замена, которая может вызвать сбой. Ничего нельзя было заменить в функции, код всегда неверен / всегда корректен в зависимости от аргументов шаблона структуры.

При создании структуры она либо всегда деформирована, либо всегда хорошо сформирована. Вы можете обойти это, наложив искусственную подстановку в функции:

template <bool f=false>
std::enable_if_t<(std::is_same<Types, int>::value ||  ...  ||f ), int> get_int() const { return get<int>(); }

Тогда она должна скомпилироваться нормально.

Подстановка происходит в

  • все типы, используемые в типе функции (включая тип возвращаемого значения и типы всех параметров)
  • все типы, используемые в объявлениях параметров шаблона
  • все выражения, используемые в тип функции
  • все выражения, используемые в объявлении параметра шаблона (начиная с C ++ 11)
  • все выражения, используемые в явном спецификаторе (начиная с C ++ 20)

Причина, по которой код лучше сжат, заключается в том, что std::enable_if_t<false, int> func() {} всегда не скомпилируется.

...