我正在尝试为我的变体具有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;
}
但在gcc9上,这给出了错误:
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的其他替换确实产生了有效语法。
我的代码出了什么问题,有没有办法得到我想要的结果?
谢谢。
尝试编译您的代码时收到的错误与您的不同,请参阅编译器资源管理器;我收到了我所期望的错误"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() {}
总是编译失败。
我找到了答案。
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;
}