C++强制变量模板的特定类型(甚至概念似乎也不起作用)

x4shl7ld  于 2022-12-24  发布在  其他
关注(0)|答案(2)|浏览(128)

如果我们有一个接受3个bool类型参数的构造函数:

struct BoolOnly {
    BoolOnly (bool b1, bool b2, bool b3)
    {}
};

编译器验证我们实际上是用布尔参数调用它:

BoolOnly b1 {0,1,1};    //ok
BoolOnly b2 {0,1,4};    //error: narrowing conversion of ‘4’ from ‘int’ to ‘bool’

如果一个值超出了布尔值的范围,它会给出一个编译器错误,这是完全可以接受的。
现在的问题是:我想有一个可变参数模板构造函数,我想只接受bool类型的参数,换句话说,我想能够传递一个只包含0或1的初始化器,如果我传递其他参数,就会得到编译器错误

struct BoolOnlyExactVariadic {
    template<typename... Args>
    BoolOnlyExactVariadic (Args... args)
    {}
};

这里我们不能指定任何类型的参数。这个构造函数接受任何类型的参数的任意计数。在C++中仍然不支持同质可变模板参数。
我们拥有的最先进的东西是概念--它们似乎是为设置可变参数模板参数的限制而设计的。

template<typename T>
concept BooleanExact = std::same_as<T, bool>;

struct BoolOnlyExact {
    template<typename... Args>
    BoolOnlyExact (BooleanExact auto... b)
    {}
};

但问题是这个模板甚至不接受布尔范围内的值:

BoolOnlyExact t2 {0,0,0}; //note: the expression ‘(... && BooleanExact<auto:12>) [with auto:12 = {int, int, int}]’ evaluated to ‘false’

参数被转换成int,这不是到bool的精确转换,让我们试试std::convertible_to:

template<typename T>
concept BooleanConv = std::convertible_to<T, bool>;

struct BoolOnlyConv {
    template<typename... Args>
    BoolOnlyConv (BooleanConv auto... b)
    {}
};

现在编译成功了,但是没有对参数值进行验证:

BoolOnlyConv t1 {4,4,5}; //ok, but we want to validate them

即使值在布尔范围之外。(不知道为什么编译器认为“5”可以转换为布尔)。
有没有C20/C23的方法可以在编译时验证一个可变初始化布尔值列表,就像第一个例子一样?除了手工写一个有15个参数的函数?这里的问题是可能有更多参数的情况(例如,9或25),对于这些情况需要单独的函数?

ewm0tg9j

ewm0tg9j1#

我会忘记将01作为布尔值传递的想法,使用truefalse,并使用std::same_as<bool>检查类型。
我看到的其他选项只有:

  • 如果所有的布尔值都是constexpr,构造函数体也是,那么可以将构造函数设为consteval,并在编译时检查整数,以确保它们在范围内。
  • 或者创建一个template <bool...> struct BoolList {};并将其传递给构造函数,但这会改变调用语法。
rekjcdws

rekjcdws2#

这不是一个很好的解决方案...但是如果布尔值的数量有上限(在下面的示例中为99),则可以递归地添加所需的唯一布尔构造函数
我们可以从一个简单的结构体开始,它接收一个数字作为模板参数,忽略它并将using类型设置为bool

template <std::size_t>
struct getBool
 { using type = bool; };

然后,您可以创建一个模板递归类AddC(用于“add constructor”)来添加仅布尔构造函数

template <typename>
struct AddC;

template <std::size_t ... Is>
struct AddC<std::index_sequence<Is...>>
  : public AddC<std::make_index_sequence<sizeof...(Is)-1u>>
{
  using AddC<std::make_index_sequence<sizeof...(Is)-1u>>::AddC;

  AddC (typename getBool<Is>::type ... as)
  { };
};

// to stop the recursion
template <>
struct AddC<std::index_sequence<>>
{ };

现在BoolOnlyExactVariadic就变成了

struct BoolOnlyExactVariadic
  : public AddC<std::make_index_sequence<100u>>
{ 
  using AddC<std::make_index_sequence<100u>>::AddC;
};

下面是一个完整的编译示例(t3变量除外,它给出了一个收缩编译错误)。

#include <utility>

template <std::size_t>
struct getBool
 { using type = bool; };

template <typename>
struct AddC;

template <std::size_t ... Is>
struct AddC<std::index_sequence<Is...>>
  : public AddC<std::make_index_sequence<sizeof...(Is)-1u>>
{
  using AddC<std::make_index_sequence<sizeof...(Is)-1u>>::AddC;

  AddC (typename getBool<Is>::type ... as)
  { };
};

// to stop the recursion
template <>
struct AddC<std::index_sequence<>>
{ };

struct BoolOnlyExactVariadic
  : public AddC<std::make_index_sequence<100u>>
{ 
  using AddC<std::make_index_sequence<100u>>::AddC;
};

int main()
{
  BoolOnlyExactVariadic t1 {0, 1, 0};             // compile
  BoolOnlyExactVariadic t2 {0, 0, 1, 1, 0, 0};    // compile
  BoolOnlyExactVariadic t3 {0, 0, 1, 1, 0, 0, 2}; // compilation error
}

相关问题