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
不在 a
和 b
之间。剩下要做的就是确保您永远不会手动设置 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> {
我在模板元编程方面遇到了一些问题。我正在尝试创建一个基于枚举 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
不在 a
和 b
之间。剩下要做的就是确保您永远不会手动设置 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> {