成员的部分专业化

时间:2017-09-24 17:36:59

标签: c++ enable-if

尝试专门化成员方法。
阅读上一个问题:std::enable_if to conditionally compile a member function
我完全明白我做错了什么。

#include <string>
#include <iostream>
#include <type_traits>

template<typename T>
class Traits
{
};

struct Printer
{
    template<typename T>
    typename std::enable_if<!std::is_function<decltype(Traits<T>::converter)>::value, void>::type
    operator()(T const& object)
    {
        std::cout << object;
    }
    template<typename T>
    typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type
    operator()(T const& object)
    {
        std::cout << Traits<T>::converter(object);
    }
};

template<>
class Traits<std::string>
{
    public:
        static std::size_t converter(std::string const& object)
        {
            return object.size();
        }
};

int main()
{
    using namespace std::string_literals;

    Printer     p;

    p(5);
    p("This is a C-string");
    p("This is a C++String"s);  // This compiles.
}

编译提供:

> g++ -std=c++1z X.cpp
X.cpp:42:5: error: no matching function for call to object of type 'Printer'
    p(5);
    ^
X.cpp:14:5: note: candidate template ignored: substitution failure [with T = int]: no member named 'converter' in 'Traits<int>'
    operator()(T const& object)
    ^
X.cpp:20:5: note: candidate template ignored: substitution failure [with T = int]: no member named 'converter' in 'Traits<int>'
    operator()(T const& object)
    ^

他们似乎都失败了,因为他们看不到方法converter。但我试图使用SFINE和std::enable_if来识别这个函数不存在,因此只实例化其中一个方法。

为每种类型生成相同的错误:

X.cpp:43:5: error: no matching function for call to object of type 'Printer'
    p("This is a C-string");
    ^
X.cpp:14:5: note: candidate template ignored: substitution failure [with T = char [19]]: no member named 'converter' in 'Traits<char [19]>'
    operator()(T const& object)
    ^
X.cpp:20:5: note: candidate template ignored: substitution failure [with T = char [19]]: no member named 'converter' in 'Traits<char [19]>'
    operator()(T const& object)
    ^

注意:它会编译std::string版本。

3 个答案:

答案 0 :(得分:1)

您可以遵循私有帮助函数,并使用重载决策来优先选择SFINAE-d过载 - 并且没有负面的SFINAE-d:

struct Printer
{
    template <class T>
    void operator()(T const& object) {
        call_impl(object, 0);
    }

private:
    // selected if Traits<T>::converter exists and is a function
    // preferred in this case because int is better than ...
    template<typename T>
    typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type
    call_impl(T const& object, int)
    {
        std::cout << Traits<T>::converter(object);
    }

    // selected if either Traits<T>::converter doesn't exist or isn't a function
    template<typename T>
    void call_impl(T const& object, ...)
    {
        std::cout << object;
    }

};

我们将在C ++ 2a中使用约束函数获得的一个好处是,我们可以在没有额外帮助的情况下完成此任务:

struct Printer
{
    template <class T>
        requires std::is_function<decltype(Traits<T>::converter)>::value
    void operator()(T const& object)
    {
        std::cout << Traits<T>::converter(object);
    }

    template <class T>
    void operator()(T const& object)
    {
        std::cout << object;
    }
};

答案 1 :(得分:0)

如何将非功能converter添加到非专业特征中?

template<typename T>
class Traits
{
    public: enum class Dummy{nothing};
    public: static Dummy const converter = Dummy::nothing;
};

Run this code online

答案 2 :(得分:0)

问题在于SFINAE的工作原理。当替换失败时,整个功能将从程序中取出。因此,即使您的谓词typename std::enable_if<!std::is_function<decltype(Traits<T>::converter)>::value, void>::type意图捕获false案例,converter的不存在也会导致重载被取消。

最简单的解决方法是这样的:

struct Printer
{
    template<typename T>
    void
    impl(T const& object, ...)
    {
        std::cout << object;
    }

    template<typename T>
    typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type
    impl(T const& object, void*)
    {
        std::cout << Traits<T>::converter(object);
    }

    template<typename T>
    void
    operator()(T const& x)
    {
        return impl(x, nullptr);
    }
};

基本上:您为编译器提供了一些在不使用谓词的情况下始终可以工作的东西。这里的诀窍是nullptr将与void*而不是...匹配,因此它会做你想要的。

如果您希望获得真正的乐趣,可以创建一个返回类型为has_convertertrue_type的{​​{1}}函数,并重置该实现。

false_type

可以想象帮助函数或模板struct Printer { template<typename T> std::false_type has_converter(T const& object, ...); template<typename T> typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, std::true_type>::type has_converter(T const& object, void*); template<typename T> void impl(T const& x, std::false_type) { std::cout << x; } template<typename T> void impl(T const& x, std::true_type) { std::cout << Traits<T>::converter(x); } template<typename T> void operator()(T const& x) { return impl(x, decltype(has_converter(x, nullptr))()); } }; 使得使用此属性更加容易(使用与上面相同的技术)。

constexpr bool