与模板化成员方法同名的普通成员方法

时间:2016-12-21 11:36:26

标签: c++ c++11

我正在尝试创建一个名为duo的课程。

duo是一个存储任何类型T对象的类,以及std::string对象的“{1}}版本。 (例如,如果TT,请说int,则字符串版本为861。)

我们的想法是拥有一个显示字符串以及一个值。我计划使用"861"进行两个VALUE之间的比较,duo将在需要时用于显示字符串。

它应该可以从NAMEstd::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有什么办法吗?我该怎么做呢?谢谢。

3 个答案:

答案 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)

这不起作用。如果Tstd::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);
};

当然,您可以使用两个构造函数或两个构造函数执行相同的操作。如果只使用一个参数构造类没有明确的结果,那么拥有清晰的语义总是一个好主意。