C++ 模板专业化:参数不同于T的成员函数

C++ 模板专业化:参数不同于T的成员函数,c++,templates,template-specialization,C++,Templates,Template Specialization,是否可以使用模板专业化来实现以下内容 #include <iostream> template< typename T, bool a, bool b> class Test { T value; public: Test() {} Test( const T& val ) : value( val ) {} void Set( const T& val ) { // [1] value = val;

是否可以使用模板专业化来实现以下内容

#include <iostream>

template< typename T, bool a, bool b>
class Test
{
    T value;

public:
    Test() {}
    Test( const T& val ) : value( val ) {}

    void Set( const T& val ) { // [1]
        value = val;
    }

    void Set( const float val ) { // [2] To run just for T != float
        ...
    }
};

int main()
{
    Test<int, true, true> test1;
    test1.Set( 1234 ); // Run [1]

    Test<float, true, true> test2;
    test2.Set( 1.234f ); // Run [1]

    Test<int, true, true> test3;
    test3.Set( 1.234f ); // Run [2]
}

当T不同于float时,是否有语法指定要选择的成员函数?

一个干净的选项是使用非多态继承进行专门化,而不重复所有操作:

namespace detail
{
    template< typename T, bool a, bool b>
    class TestImplBase
    {
    protected:
        T value;

    public:
        TestImplBase() {}
        TestImplBase( const T& val ) : value( val ) {}

        void Set( const T& val ) { // [1]
            value = val;
        }
    };

    // General case
    template< typename T, bool a, bool b>
    class TestImpl : public TestImplBase<T, a, b>
    {
    public:
        using TestImplBase<T, a, b>::TestImplBase; // keep the same constructors
        using TestImplBase<T, a, b>::Set; // See comment by Jarod42

        // Has [1] as well.

        void Set( const float val ) { // [2] To run just for T != float
            //...
        }
    };

    // Specialization for T == float
    template<bool a, bool b>
    class TestImpl<float, a, b> : public TestImplBase<float, a, b>
    {
    public:
        using TestImplBase<float, a, b>::TestImplBase; // keep the same constructors

        // Only has [1], not [2].
    };
}

template< typename T, bool a, bool b>
using Test = detail::TestImpl<T, a, b>;

这是一种非常通用的方法,对于您的情况来说可能有点过于通用,但从这一小段代码中很难说出来。

一个干净的选择是使用非多态继承来进行专门化,而不重复所有内容:

namespace detail
{
    template< typename T, bool a, bool b>
    class TestImplBase
    {
    protected:
        T value;

    public:
        TestImplBase() {}
        TestImplBase( const T& val ) : value( val ) {}

        void Set( const T& val ) { // [1]
            value = val;
        }
    };

    // General case
    template< typename T, bool a, bool b>
    class TestImpl : public TestImplBase<T, a, b>
    {
    public:
        using TestImplBase<T, a, b>::TestImplBase; // keep the same constructors
        using TestImplBase<T, a, b>::Set; // See comment by Jarod42

        // Has [1] as well.

        void Set( const float val ) { // [2] To run just for T != float
            //...
        }
    };

    // Specialization for T == float
    template<bool a, bool b>
    class TestImpl<float, a, b> : public TestImplBase<float, a, b>
    {
    public:
        using TestImplBase<float, a, b>::TestImplBase; // keep the same constructors

        // Only has [1], not [2].
    };
}

template< typename T, bool a, bool b>
using Test = detail::TestImpl<T, a, b>;
这是一种非常通用的方法,对于您的案例来说可能有点太通用了,但是从这一小段代码中很难说出来

是否可以使用模板专业化来实现以下内容

是:专门化完整类测试:泛型用例,带[2],浮点用例,不带[2]

否则,我想当T是float时,可以使用SFINAE禁用[2]

或者也可能

template <typename U = T>
std::enable_if_t<false == std::is_same_v<U, float> && true == std::is_same_v<U, T>>
      Set( const float val )
 { /* ... */ }
如果要避免启用该集合,请在浮点情况下,说明U模板类型

是否可以使用模板专业化来实现以下内容

是:专门化完整类测试:泛型用例,带[2],浮点用例,不带[2]

否则,我想当T是float时,可以使用SFINAE禁用[2]

或者也可能

template <typename U = T>
std::enable_if_t<false == std::is_same_v<U, float> && true == std::is_same_v<U, T>>
      Set( const float val )
 { /* ... */ }

如果您想避免启用该集合,在浮点情况下,解释U模板类型。

使用C++20,它将非常简单:

需要允许放弃方法:

template <typename T, bool a, bool b>
class Test
{
    T value;
public:
    Test() {}
    Test(const T& val) : value( val ) {}

    void Set(const T& val) { value = val; }

    void Set(float val) requires (!std::is_same<T, float>::value) {
        // ...
    }
};

否则,SFINAE通常是删除重载的方法,但需要模板函数,因此您必须制作一个方法模板。

使用C++20,它将非常简单:

需要允许放弃方法:

template <typename T, bool a, bool b>
class Test
{
    T value;
public:
    Test() {}
    Test(const T& val) : value( val ) {}

    void Set(const T& val) { value = val; }

    void Set(float val) requires (!std::is_same<T, float>::value) {
        // ...
    }
};

否则SFINAE通常是删除重载的方法,但需要模板功能,因此您必须制作一个方法模板。

只是因为我在另一个选项卡中打开了它:。链接只回答这不是一个选项,但我假设在某个地方有一个重复。只是因为我在另一个选项卡中打开了它:。链接只回答这不是一个选项,但我认为有一个重复的地方。它的工作!问题:为什么我不能再为函数指定返回类型?另一个问题:在类块外实现函数的语法是什么?@Pietro必须将返回类型指定为std::enable_if_t的第二个参数。请参阅我在评论中发布的链接,并查看该链接以了解为什么这不是首选解决方案。@Pietro是的,但如果尚未启用,则这是默认设置。虽然我想显式地指定它并没有什么坏处。@Pietro您需要两个模板,是的。您还需要重复所有SFINAE的内容,因为它是一个模板,所以它必须在标题中。因此,我认为在类之外定义函数并不会给你带来太多好处,你只需要重复所有签名的内容就行了。它是有效的!问题:为什么我不能再为函数指定返回类型?另一个问题:在类块外实现函数的语法是什么?@Pietro必须将返回类型指定为std::enable_if_t的第二个参数。请参阅我在评论中发布的链接,并查看该链接以了解为什么这不是首选解决方案。@Pietro是的,但如果尚未启用,则这是默认设置。虽然我想显式地指定它并没有什么坏处。@Pietro您需要两个模板,是的。您还需要重复所有SFINAE的内容,因为它是一个模板,所以它必须在标题中。因此,我认为在类之外定义函数并不会给您带来太多好处,您只需重复所有签名内容。几乎,但您调用了错误的重载,因为[2]隐藏了[1]。您需要使用TestImplBase::Set@Jarod42,谢谢您发现了这一点!更正了答案。接近,但您调用了错误的重载,因为[2]隐藏了[1]。您需要使用TestImplBase::Set@Jarod42,谢谢您发现了这一点!更正了答案。很好的简化。很好的简化。