【问题标题】:std::enable_if cannot be used to disable this declarationstd::enable_if 不能用于禁用此声明
【发布时间】:2019-06-04 13:07:28
【问题描述】:

我有一段相当复杂的代码,我将其简化为这个复制器:

#include <type_traits>
#include <tuple>
template<typename ...As>
struct outer {
    template<typename ...Bs>
    struct inner {
        template<bool dummy, typename E = void>
        struct problem;
        using TA = std::tuple<As...>;
        using TB = std::tuple<Bs...>;

        template<bool dummy>
        struct problem<dummy, typename std::enable_if<std::tuple_size<TA>::value < std::tuple_size<TB>::value>::type>
        {
            static constexpr auto val() { return 1; } // actually a complex function
        };

        template<bool dummy>
        struct problem<dummy, typename std::enable_if<std::tuple_size<TA>::value >= std::tuple_size<TB>::value>::type>
        {
            static constexpr auto val() { return 0; }
        };
    };
};

int main() {
    return outer<int, float>::inner<double>::problem<false>::val();
}

它不编译(使用 gcc 或 clang),说:

<source>:13:82: error: failed requirement 'std::tuple_size<std::tuple<int, float> >::value < std::tuple_size<std::tuple<double> >::value'; 'enable_if' cannot be used to disable this declaration

        struct problem<dummy, typename std::enable_if<std::tuple_size<TA>::value <std::tuple_size<TB>::value>::type>

                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~

<source>:27:31: note: in instantiation of template class 'outer<int, float>::inner<double>' requested here

    return outer<int, float>::inner<double>::problem<false>::val();

我尝试了一些变体,但没有任何效果。

我阅读了一些已经发布的问答,例如: this onethis one 但他们似乎没有回答我的问题。

PS:我可以使用 C++17,但它必须适用于任何编译器。

【问题讨论】:

  • 原因是需求不是problem的实例化中的依赖表达式。在这个具体的例子中,可以让val 只返回std::tuple_size&lt;std::tuple&lt;int, float&gt; &gt;::value &lt; std::tuple_size&lt;std::tuple&lt;double&gt; &gt;::value ? 1 : 0
  • 不,这是不可能的。 val 实际上是一个复杂的函数
  • 您可能想查看“if constexpr”,这会产生比 enable_if 更简单的代码。

标签: c++ variadic-templates inner-classes sfinae typetraits


【解决方案1】:

建议:尝试以下方法

struct inner {
    using TA = std::tuple<As...>;
    using TB = std::tuple<Bs...>;

    template<bool dummy, typename UA = TA, typename E = void>
    struct problem;

    template<bool dummy, typename UA>
    struct problem<dummy, UA,
       std::enable_if_t<(std::tuple_size_v<UA> < std::tuple_size_v<TB>)>>
     { static constexpr auto val() { return 1; } };

    template<bool dummy, typename UA>
    struct problem<dummy, UA,
       std::enable_if_t<(std::tuple_size_v<UA> >= std::tuple_size_v<TB>)>>
     { static constexpr auto val() { return 0; } };
};

我的意思是...考虑到 SFINAE 与您想要启用/禁用的结构/类(或函数或方法)的模板参数的测试一起工作。

原始代码中的问题是 SFINAE 测试仅考虑 TATB 是在包含 probleminner 结构中定义的类型。所以测试只依赖于外部模板参数(As...Bs...),而不依赖于problem 的模板参数。

problem添加默认值的模板参数

// ..................VVVVVVVVVVVVVVVVV
template<bool dummy, typename UA = TA, typename E = void>
struct problem;

不会改变problem 本身的实际用途,而是改变std::enable_if 中的测试

template<bool dummy, typename UA>
struct problem<dummy, UA, // ..........VV  UA, not TA
   std::enable_if_t<(std::tuple_size_v<UA> < std::tuple_size_v<TB>)>>
 { static constexpr auto val() { return 1; } };

在涉及problem 本身的模板参数的测试中。

【讨论】:

  • 好吧。谢谢你。如何引入虚假依赖。
  • @RegisPortalez - 另请参阅其他答案(以及 Unimportant 的重要评论):我的答案在 C++11 中有效(几乎没有修改),并且(我希望)解释为什么对你的无效原始代码。但是,如果您可以使用 C++17,if constexpr(可能在这种情况下;当然在其他情况下)允许更简单、更优雅的解决方案。
【解决方案2】:

正如@unimportant 评论的那样:if-constexpr 自 C++17 起。让您摆脱dummy 和更多行:

#include <type_traits>
#include <tuple>

template<typename ...As>
struct outer {
    template<typename ...Bs>
    struct inner {
        using TA = std::tuple<As...>;
        using TB = std::tuple<Bs...>;

        static constexpr auto val() { 
            if constexpr (std::tuple_size_v<TA> >= std::tuple_size_v<TB>) {
                return 0;
            }
            else {
                return 1;
            }
        }
    };
};

int main() {
    return outer<int, float>::inner<double>::val();
}

程序“[4544] main.exe”已退出,代码为 0 (0x0)。

【讨论】:

    【解决方案3】:

    正如 cmets 中所建议的,使用 if constexpr(C++17 的一部分)使代码更简单:

    template<typename ...As>
    struct outer {
        template<typename ...Bs>
        struct inner {
            using TA = std::tuple<As...>;
            using TB = std::tuple<Bs...>;
    
            struct problem
            {
                static constexpr auto val()
                {
                    if constexpr (std::tuple_size<TA>::value < std::tuple_size<TB>::value)
                        return 1; // Complex function goes here
                    else
                        return 0; // Other complex function (?) goes here
                }
            };
        };
    };
    

    Demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-18
      • 1970-01-01
      • 2018-11-15
      • 1970-01-01
      • 1970-01-01
      • 2022-11-20
      • 1970-01-01
      • 2020-11-15
      相关资源
      最近更新 更多