【问题标题】:Outputtable trait in C++: SFINAE always picks one implementation over the otherC++ 中的可输出特征:SFINAE 总是选择一种实现而不是另一种
【发布时间】:2020-04-01 11:49:42
【问题描述】:

我在 S.O 上关注了一个关于如何实现 Outputtable trait 类以在编译时检查是否可以在 std::ostream 上输出类型的线程。该类的实现如下:

template<typename U>
        struct OstreamOutputableTrait
        {
            template<typename T>
            static decltype(std::declval<std::ostream&>() << std::declval<T>(), std::true_type{} )
            IsOstreamOutputtable(std::ostream& os, const T& var) {}

            // for vector<T>
            template<typename T>
            static std::false_type IsOstreamOutputtable(std::ostream& os, const std::vector<T>& var)
            {}

            template<typename > static auto IsOstreamOutputtable(...) {
                return std::false_type {};
            }
            static const auto value =
                    decltype(IsOstreamOutputtable(std::declval<std::ostream&>(), std::declval<U>()))::value;
        };

// operator implementation
        template<typename Key, typename T>
        std::ostream& operatorImpl(std::ostream& os, const std::map<Key,T>& map, const std::true_type& ) {
            for(auto it = map.begin(); it!= map.end(); ++it)
                os << "(" << (*it).first << " ; " << (*it).second << ")" <<"\n";
            return os;
        }

        template<typename Key, typename T>
        std::ostream& operatorImpl(std::ostream& os, const std::map<Key, T>& map, const std::false_type& ) {
            os << "\nElements of the map are not printable.\n";
            return os;
        }

        // operator << on <Key, value> maps
        template<typename Key, typename T>
        std::ostream& operator<<(std::ostream& os, const std::map<Key, T>& map)
        {
            // redirect using SFINAE to correct Impl of the operator<< : ty always returns true!
            auto ty = std::integral_constant<bool,
            OstreamOutputableTrait<typename std::decay_t<T> >::value &&
            OstreamOutputableTrait<typename std::decay_t<Key> >::value>();

            operatorImpl(os, map, ty);
            return os;
        }

我遇到的问题是上面函数中的变量ty 总是返回std::true_type。这是否意味着std::map 模板类的&lt;Key,T&gt; 类型名在STL 中默认可以在std::ostream 上输出?如果有人能解释我可能做错了什么,那将对我有很大帮助。

谢谢 胺

【问题讨论】:

    标签: c++ templates sfinae


    【解决方案1】:

    您的错误在于声明 IsOstreamOutputtable() 的可变参数版本

    template <typename>
    static auto IsOstreamOutputtable (...)
     { return std::false_type {}; }
    

    您将其声明为 模板 函数,并带有无法从参数中推导出来的模板参数。

    所以,当你从decltype()调用函数时

    static const auto value =
       decltype(IsOstreamOutputtable(std::declval<std::ostream&>(), std::declval<U>()))::value;
    

    编译器无法考虑可变参数版本。

    解决方案。

    (1) 可以显式调用IsOstreamOutputtable()的模板参数

       // ..........................VVV   explicit template parameter
       decltype(IsOstreamOutputtable<U>(std::declval<std::ostream&>(), std::declval<U>()))::value;
    

    所以编译器可以使用可变参数版本,

    问题:此解决方案不适用于std::vector 版本

    (2)(更好)您可以将IsOstreamOutputtable() 的可变参数版本设为非模板版本

    // no needs of template
    static auto IsOstreamOutputtable (...)
     { return std::false_type {}; }
    

    题外话:IsOstreamOutputtable() 方法只在decltype() 内部调用,所以不需要定义,你只需要声明它们。 (如果您定义它们,请保持连贯:对于返回 std::true_type 的方法,主体不能为空)。

    使用尾随返回类型和其他小的简化,您的打字可以减少一点

    template <typename U>
    struct OstreamOutputableTrait
     {
       template <typename T>
       static auto IsOstreamOutputtable (T const & var)
          -> decltype( std::declval<std::ostream&>() << var, std::true_type{} );
    
       // special case for vector<T>
       template <typename T>
       static std::false_type IsOstreamOutputtable (std::vector<T> const &);
    
       static std::false_type IsOstreamOutputtable (...);
    
       static constexpr auto value
          = decltype(IsOstreamOutputtable(std::declval<U>()))::value;
     };
    

    【讨论】:

    • 非常感谢@max66!从现在开始,我将遵循您的指导方针。因此,如果我理解正确,在函数的可变参数 template 定义中不使用任何参数,编译器会将其视为非模板函数,因此在 SFINAE 机制的编译时被忽略?谢谢
    • @Amine - 抱歉,但我不明白“在函数的可变参数模板 定义中不使用参数”是什么意思。请问,你能写一个实际的例子(也许在问题中)吗?
    【解决方案2】:

    @max66,

    我的想法是:

    template<typename T>
                static decltype(std::declval<std::ostream&>() << std::declval<T>(), std::true_type{} )
                IsOstreamOutputtable(std::ostream& os, const T& var) {}
    

    将检查可以使用operator&lt;&lt; 输出的每种类型T,decltype 将返回std::true_type 的类型。在这种情况下,可变参数重载:

    template<typename > static auto IsOstreamOutputtable(...) {
                    return std::false_type {};
                }
    

    没有任何用处,只会引起混乱。话虽如此,如果我使用语法:

    decltype(IsOstreamOutputtable&lt;U&gt;(std::declval&lt;std::ostream&amp;&gt;(), std::declval&lt;U&gt;()))::value;

    可变参数函数会优先于第一个返回std::true_type 的函数吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-15
      • 2011-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-26
      • 2011-10-23
      • 1970-01-01
      相关资源
      最近更新 更多