Специализация шаблона: функция-член с параметром, отличным от T - PullRequest
1 голос
/ 10 октября 2019

Можно ли реализовать что-то вроде того, что следует, используя специализацию шаблонов?

#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?

Ответы [ 3 ]

1 голос
/ 10 октября 2019

Можно ли реализовать что-то вроде следующего, используя специализацию шаблонов?

Да: специализация в полном объеме class Test: общий случай, с "[2]" и float case, без него.

В противном случае, я полагаю, вы можете использовать SFINAE для отключения "[2]", когда T равно float

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

Или, возможно, также

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 )
 { /* ... */ }

, если вы хотите избежать включения Set(), в случае float, объясняя тип шаблона U.

1 голос
/ 11 октября 2019

С C ++ 20 было бы очень просто:

requires разрешить "отбрасывать" метод:

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 - это, как правило, способ снять перегрузку(но требует функции шаблона, поэтому вы должны сделать один из шаблонов вашего метода).

1 голос
/ 10 октября 2019

Чистым вариантом будет использование (неполиморфного) наследования для выполнения специализации без повторения всего:

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>;

https://godbolt.org/z/zRieA2

Это очень общий подход, возможно,Слишком общий для вашего случая, но это трудно сказать по этому небольшому куску кода.

...