我正在尝试创建一个名为duo的类。
duo是一个类,用于存储任何类型T的对象以及std::string,后者是T对象的“文本”版本。 (例如,如果Tint,例如861,则其字符串版本将是"861"。)

这个想法是要有一个显示字符串和一个值。我计划使用VALUE在两个duo之间进行比较,并且在需要时将NAME用于显示字符串。

它应该能够从std::stringT构造。换句话说,如果我说

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_ifstd::is_same必须由您自己编写或从STL复制。

要回答您的评论:

如果使用任何类型的模板的实例,则将获得该模板描述的“事物”的实例。模板类的新模板实例将为该实例生成新代码。模板构造函数相同。但是开销与手工编写代码相同。 SFINAE不会产生任何开销!所有 Action 都在编译时完成!

10-08 08:28
查看更多