我正在尝试创建一个名为duo
的课程。
duo
是一个存储任何类型T
对象的类,以及std::string
对象的“{1}}版本。 (例如,如果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
有什么办法吗?我该怎么做呢?谢谢。
答案 0 :(得分:5)
您需要模板专业化。请参阅以下代码:
template< class T >
class duo
{
private:
T VALUE;
std::string NAME;
public:
// Templated version
duo( const T& value ) : NAME(value), VALUE(value) // please make sure that NAME(value) works!
{
std::cout<<"template version";
}
};
template<>
class duo<std::string>
{
private:
std::string VALUE;
std::string NAME;
public:
// Normal version
duo( const std::string& name ): NAME(name), VALUE(name)
{
std::cout<<"normal version";
}
};
int main()
{
duo<std::string> d(std::string("abc")); // normal version
// duo<int> d2(1); // templated version
return 0;
}
duo<int> d2(1)
不起作用,因为int
无法隐式转换为std::string
。这是你接下来应该做的。
以下是链接:http://en.cppreference.com/w/cpp/language/template_specialization
修改强>
如果你想专门化构造函数:
template< class T >
class duo
{
private:
T VALUE;
std::string NAME;
public:
// Templated version
template<class V>
duo(const V& value)
{
std::cout<<"template version";
}
// Normal version
duo(const std::string &name)
{
std::cout<<"normal version";
}
};
答案 1 :(得分:2)
如果您想要构造函数的两个版本,但只有一个类定义,我相信在您编辑问题之后,您可以决定每个实现与SFINAE一起使用的参数类型。
如果我们编写OP问题中给出的两个版本的构造函数,会出现什么问题:
duo( const T& value );
duo( const std::string& name );
在T为std::string
的情况下,这将失败,因为我们现在必须使用相同的方法签名,因为模板实例化为T = std :: string,因此我们有两次签名:{{ 1}}
SFINAE使我们能够使模板无法初始化,因此在实例化中是“不可见的”。那么我们想要实现的目标:
duo( const std::string& name );
如果构造函数的参数与为其创建的类相同,则应始终使用此签名。因此,如果T为int且输入参数为int,则应采用此版本。这可以通过以下方式实现:
duo( const T& value );
但是如果我们输入template <typename X, typename std::enable_if< std::is_same<T,X>::value,
int>::type* = nullptr >
duo( const X& value ): VALUE(value);
,我们将使用转换构造函数。
但请记住,有一个特殊情况,其中T是std::string
,我们不想使用这个转换构造函数,因为我们不需要转换!所以我们也想要上面的版本。如此简单地使用SFINAE使该版本仅在T不是std::string
时才有效!
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 );
SFINAE也可以不使用c ++ 11,但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;
}
和std::enable_if
必须由您自己编写或者只是从STL复制。
回答你的评论:
如果您使用任何类型模板的实例,您将获得模板描述的“事物”的实例。模板类的新模板实例将为该实例生成新代码。模板构造函数也是如此。但是开销与手动编写代码相同。 SFINAE不会产生任何开销!所有操作都在编译期间完成!
答案 2 :(得分:1)
这不起作用。如果T
为std::string
,则您定义了两个具有相同签名的构造函数:
duo.cpp:22:20: required from here
duo.cpp:18:9: error: ‘duo<T>::duo(const string&) [with T = std::__cxx11::basic_string<char>; std::__cxx11::string = std::__cxx11::basic_string<char>]’ cannot be overloaded
duo.cpp:15:9: error: with ‘duo<T>::duo(const T&) [with T = std::__cxx11::basic_string<char>]’
如果您想要这种区别,一种解决方案是将标记参数添加到非模板化版本:
duo( const std::string& name, bool /* anonymous, not really used */ );
如果它被认为是某种特殊情况,并且您想明确表示您正在初始化不同的财产,而不是使用&#34;正常&#34;构造函数,您也可以将构造函数设为私有,并使用工厂方法来调用它:
class duo {
// ...
public:
static duo fromName(const std::string& name) {
return {name, true};
}
private:
duo(const std::string& name, bool);
};
当然,您可以使用两个构造函数或两个构造函数执行相同的操作。如果只使用一个参数构造类没有明确的结果,那么拥有清晰的语义总是一个好主意。