我正在尝试创建一个名为duo
的类。duo
是一个类,用于存储任何类型T
的对象以及std::string
,后者是T
对象的“文本”版本。 (例如,如果T
是int
,例如861
,则其字符串版本将是"861"
。)
这个想法是要有一个显示字符串和一个值。我计划使用VALUE
在两个duo
之间进行比较,并且在需要时将NAME
用于显示字符串。
它应该能够从std::string
或T
构造。换句话说,如果我说
std::string num("861");
duo<int> anumber(num);
就像我一样会创建相同的对象
duo<int> anumber(861);
其目的是转换和存储两者,以便以后可以使用它们而不必再次调用转换函数。我需要能够同时访问“值”和“名称”。
如果
duo
是使用“普通版本”(传递std::string
)构造的,则VALUE
将由name
转换。如果使用“模板版本”(传递类型为T
的对象)构造,则NAME
将由value
转换。template< class T >
class duo
{
private:
T VALUE;
std::string NAME;
public:
// Templated version
duo( const T& value );
// Normal version
duo( const std::string& name );
};
T
有什么办法可以成为std::string
吗?我将如何去做呢?谢谢。 最佳答案
如果您想要构造器的两个版本,但只希望一个类定义,这是我相信在编辑问题后所能遵循的定义,则可以确定每种实现都适用于SFINAE的参数类型。
如果我们像OP问题中那样编写两个版本的构造函数,那会是什么问题:
duo( const T& value );
duo( const std::string& name );
在T为
std::string
的情况下,这将失败,因为我们现在必须对方法的相同签名进行计时,因为模板实例化为T = std::string,因此我们对签名进行了两倍的签名:duo( const std::string& name );
SFINAE使我们能够使模板无法初始化,因此实例化为“不可见”。所以我们要实现:
duo( const T& value );
如果构造函数的参数与为其创建的类相同,则应始终使用此签名。因此,如果T为int且输入参数为int,则应采用此版本。这可以通过以下方式实现:
template <typename X, typename std::enable_if< std::is_same<T,X>::value,
int>::type* = nullptr >
duo( const X& value ): VALUE(value);
但是,如果我们将
std::string
作为输入,则将使用转换构造函数。但请记住,在特殊情况下,T是
std::string
,我们不想使用此转换构造函数,因为我们不需要转换!所以我们也想要上面的版本。如此简单地使用SFINAE来使该版本仅在T不是std::string
时才有效!template <typename X=T, typename
std::enable_if<!std::is_same<X,std::string>::value, int>::type*
= nullptr >
duo( const std::string& name );
完整的工作示例,其中包含所有经过测试的案例:
template< class T >
class duo
{
private:
T VALUE;
std::string NAME;
public:
// Templated version
template <typename X, typename std::enable_if< std::is_same<T,X>::value, int>::type* = nullptr >
duo( const X& value ): VALUE(value)
{
std::cout << "Templated version" << std::endl;
std::cout << __PRETTY_FUNCTION__ << value << std::endl;
}
// Normal version
// should NOT work if type is std::string, because in this case version above is simplier!
template <typename X=T, typename std::enable_if<!std::is_same<X,std::string>::value, int>::type* = nullptr >
duo( const std::string& name )
{
std::cout << "Normal version" << std::endl;
std::cout << __PRETTY_FUNCTION__ << name << std::endl;
std::istringstream is(name);
is >> VALUE;
}
T GetVal()
{
return VALUE;
}
};
int main()
{
duo<int> int1(1);
duo<int> int2("123");
duo<double> double1(1.234);
duo<double> double2("9.876");
duo<std::string> string1(std::string("Hallo"));
std::cout << int1.GetVal() << std::endl;
std::cout << int2.GetVal() << std::endl;
std::cout << double1.GetVal() << std::endl;
std::cout << double2.GetVal() << std::endl;
std::cout << string1.GetVal() << std::endl;
}
SFINAE也可以在没有c++ 11的情况下工作,但是
std::enable_if
和std::is_same
必须由您自己编写或从STL复制。要回答您的评论:
如果使用任何类型的模板的实例,则将获得该模板描述的“事物”的实例。模板类的新模板实例将为该实例生成新代码。模板构造函数相同。但是开销与手工编写代码相同。 SFINAE不会产生任何开销!所有 Action 都在编译时完成!