【问题标题】:Using static constexpr member variables as template method parameters使用静态 constexpr 成员变量作为模板方法参数
【发布时间】:2021-07-03 18:58:19
【问题描述】:

我试图通过将有关类的一些信息定义为静态 constexpr 成员来简化一些模板代码。这是一个非常简化的示例:

template <typename _Tp>
class Test
{
public:
    static constexpr bool is_array = std::is_array_v<_Tp>;

    template <typename U = _Tp, typename std::enable_if_t<!is_array>>* dummy = nullptr>
    void print()
    {
        std::cout << "It's not an array\n";
    }

    template <typename U = _Tp, typename std::enable_if_t<is_array>>* dummy = nullptr>
    void print()
    {
        std::cout << "It's an array\n";
    }
};

int main()
{
    Test<char[]>().print();
    Test<char>().print();
}

叮当说

/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:2385:44: error: no type named 'type' in 'std::enable_if<false, void>'; 'enable_if' cannot be used to disable this declaration
    using enable_if_t = typename enable_if<_Cond, _Tp>::type;
                                           ^~~~~
test.cc:11:49: note: in instantiation of template type alias 'enable_if_t' requested here
    template <typename U = _Tp, typename = std::enable_if_t<!is_array>>
                                                ^
test.cc:26:5: note: in instantiation of template class 'Test<char []>' requested here
    Test<char[]>().print();

如果我完全符合is_array 的条件,它就可以工作,就像Test&lt;U&gt;::is_array 一样,但是我有几个模板参数,这样做似乎会让事情变得更加混乱。

除了使用宏之外,还有什么方法可以做我所追求的吗?

【问题讨论】:

  • 单个print() 里面有if constexpr 分支怎么样?
  • @bipll 是的,在这种情况下可以。我必须使用 if constexpr 将一堆 SFINAE 选择的方法组合成大型方法。不过,它确实可以编译和工作。我正在寻找一种方法让编译器选择正确的方法。
  • 我最终重构了我的代码以更好地使用 if constexpr,这似乎是做我想做的最干净的方式。

标签: c++ templates sfinae


【解决方案1】:

如果您希望 SFINAE 适用于类的方法,则必须基于该方法的特定模板参数进行测试(在您的情况下为 U)。

如果你检查is_array,如果依赖于_Tp(类的模板参数),而不是来自U

但是,如果你至少可以使用 C++14,你可以有模板变量,所以你可以模板化is_array

template <typename U>
static constexpr bool is_array = std::is_array_v<U>;

这简化了一点

您还可以删除std::enable_if_t 之前的typename(感谢_t)并取消dummy(未使用)。

以下是完整的编译示例

#include <iostream>
#include <type_traits>

template <typename T>
struct Test
 {
   template <typename U>
   static constexpr bool is_array = std::is_array_v<U>;

   template <typename U = T,
             std::enable_if_t<not is_array<U>>* = nullptr>
   void print()
    { std::cout << "It's not an array\n"; }

   template <typename U = T,
             std::enable_if_t<is_array<U>>* = nullptr>
   void print()
    { std::cout << "It's an array\n"; }
 };

int main()
{
    Test<char[]>().print();
    Test<char>().print();
}

另一种选择(也许更好)是使用bool 模板参数而不是U;例如

template <typename T>
struct Test
 {
   static constexpr bool is_array = std::is_array_v<T>;

   template <bool B = is_array,
             std::enable_if_t<not B>* = nullptr>
   void print()
    { std::cout << "It's not an array\n"; }

   template <bool B = is_array,
             std::enable_if_t<B>* = nullptr>
   void print()
    { std::cout << "It's an array\n"; }
 };

【讨论】:

  • 这行得通,而且不会太远。我有几个想要使用的布尔值,并希望避免将它们全部传入。如果我使用模板 constexpr 进行,我将不得不传入几种类型,这仍然会变得混乱,但我我想可以缩短更复杂的表达式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-26
  • 1970-01-01
  • 2021-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多