static_assert 导致程序无法编译,即使断言位于函数模板的 header 中

static_assert causing program to not compile even though the assert is in the header of a function template

我在模板元编程方面遇到了一些问题。我正在尝试创建一个基于枚举 class 自动调整数组的模板化函数,并且我试图使用替换失败来执行此操作不是错误。我似乎在设置不明确的重载,而且我的替换失败似乎不是错误,而是无法正常工作。

代码如下:

namespace Interface{

enum class Interfaces { WIFI, MQTT, BT, SERIAL }; 

template<int lower, int upper, int val, typename rtype>
struct ibounded{ 
    static_assert(lower < val); 
    static_assert(upper > val); 
    using TYPE = rtype; 
};

char filename[3 + sizeof(Interface::Interfaces)] = {'I', 'F'}; 

template<int filenum_t, int pos> inline auto _init_filename() -> typename ibounded<0, 9, filenum_t, void>::TYPE {
    Interface::filename[pos] = static_cast<char>(filenum_t) % 10;  
}; 

template<int filenum_t, int pos> inline auto _init_filename() -> typename ibounded<10, 99, filenum_t, void>::TYPE {
    Interface::filename[pos] = static_cast<char>(filenum_t); 
    _init_filename<filenum_t / 10, pos + 1>(); 
};

template<Interface::Interfaces I> 
void _init_filename() {
    _init_filename<static_cast<int>(I), 2>(); 
}; 

template<Interface::Interfaces I> bool enabled(){
    Interface::filename[0] = 'I'; 
    Interface::filename[1] = 'F';  
    _init_filename<I>(); 
    return true; 
}


};


#include <iostream>

using namespace std; 

int main(){

Interface::enabled<Interface::Interfaces::SERIAL>(); 

cout << Interface::filename << endl; 

}; 

我想要实现的是有一个枚举 class 和一个数组,并自动获取枚举类型的数字值,并使用模板函数将其放在数组的末尾。所以,如果我有

enabled<Interface::Interfaces::SERIAL>()

我希望 Interface::filename 成为“IF3”。或者,如果枚举大于 10,则将文件名变为“IF03”。

我得到的错误是

test.cpp: In instantiation of ‘struct Interface::ibounded<10, 99, 3, void>’:
test.cpp:29:46:   required by substitution of ‘template<int filenum_t, int pos> typename Interface::ibounded<10, 99, filenum_t, void>::TYPE Interface::_init_filename() [with int filenum_t = 3; int pos = 2]’
test.cpp:36:43:   required from ‘void Interface::_init_filename() [with Interface::Interfaces I = Interface::Interfaces::SERIAL]’
test.cpp:42:22:   required from ‘bool Interface::enabled() [with Interface::Interfaces I = Interface::Interfaces::SERIAL]’
test.cpp:56:51:   required from here
test.cpp:18:25: error: static assertion failed
   18 |     static_assert(lower < val);
      |                   ~~~~~~^~~~~
test.cpp: In instantiation of ‘void Interface::_init_filename() [with Interface::Interfaces I = Interface::Interfaces::SERIAL]’:
test.cpp:42:22:   required from ‘bool Interface::enabled() [with Interface::Interfaces I = Interface::Interfaces::SERIAL]’
test.cpp:56:51:   required from here
test.cpp:36:43: error: call of overloaded ‘_init_filename<3, 2>()’ is ambiguous
   36 |     _init_filename<static_cast<int>(I), 2>();
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
test.cpp:25:46: note: candidate: ‘typename Interface::ibounded<0, 9, filenum_t, void>::TYPE Interface::_init_filename() [with int filenum_t = 3; int pos = 2; typename Interface::ibounded<0, 9, filenum_t, void>::TYPE = void]’
   25 | template<int filenum_t, int pos> inline auto _init_filename() -> typename ibounded<0, 9, filenum_t, void>::TYPE {
      |                                              ^~~~~~~~~~~~~~
test.cpp:29:46: note: candidate: ‘typename Interface::ibounded<10, 99, filenum_t, void>::TYPE Interface::_init_filename() [with int filenum_t = 3; int pos = 2; typename Interface::ibounded<10, 99, filenum_t, void>::TYPE = void]’
   29 | template<int filenum_t, int pos> inline auto _init_filename() -> typename ibounded<10, 99, filenum_t, void>::TYPE {
      |                   

非常感谢任何帮助!

it seems as if my substitution failure is not an error is not correctly working.

规则是“替换失败不是错误”。然而,这仅适用于寻找类型的过程。

这里发生的事情是,找到一个匹配的类型实际上是成功的,但是它落在了一个恰好有失败的类型上 static_assert(),这会导致编译错误。

从来没有任何替换失败被视为“不是错误”。

对于要在 SFINAE 中使用的 ibounded 类型,当条件未通过时,它根本不存在。

由于您正在努力解决这个问题,我认为有必要研究一下 ibounded 工作需要什么。但是,在实践中有一种更简单的方法可以做到这一点,详情请参见下文。

SFINAE 手册:(出于好奇)

// First declare ibounded without defining it
template<int lower, 
         int upper, 
         int val, 
         typename rtype, 
         typename pass=std::true_type>  // <---- magic sauce here
struct ibounded;


// Add a partial specialization for which the pass parameter will be set to true or false depending on the condition.
template<int lower, int upper, int val, typename rtype>
struct ibounded<lower, 
                upper, 
                val, 
                rtype, 
                std::integral_constant<bool, (lower < val && upper > val)>> {
  using TYPE = rtype; 
};

这里的关键点是在原始声明中为 pass 参数设置默认值。因此,任何使用 ibounded<a, b, c, T> 类型的尝试实际上都是在尝试使用 ibounded<a, b, c, T, std::true_type>.

现在,进入偏专业化。它定义了以下类型:

  • ibounded<a, b, c, void, std::true_type>,但仅当a < c < b
  • ibounded<a, b, c, void, std::false_type>,但仅当a >= c >= b

不存在 ibounded<a, b, c, T, std::true_type> 的定义,其中 c 不在 ab 之间。剩下要做的就是确保您永远不会手动设置 pass 参数的值,并且 ibounded 可以作为 SFINAE 测试使用。

更简单的方法: std::enable_if_t<bool, T> 是一种类型,如果 bool 为真,则作为 T 存在,否则不存在。所以你可以使用:

template<int lower, int upper, int val, typename rtype>
using ibounded = std::enable_if_t<(lower < val && upper > val), rtype>;

成为 rtype 也使它的使用变得更好,因为您不必查找类型:

template<int filenum_t, int pos> inline auto _init_filename() -> ibounded<0, 9, filenum_t, void> {