我编写了一些代码,它能够根据调用站点提供与给定函数关联的字符串(通过tuple
函数指针和并行数组)来调度函数。dispatch 函数不是直接接受字符串,而是接受一个Callable
类型,其中 aconst char*
可以转换为 a Callable
。
Callable
is的构造函数,并使用基本递归搜索constexpr
从注释中查找函数。tuple
我已经验证构造函数能够正常工作并创建一个constexpr
Callable
(包括示例)。由于调度函数接收要传递给Callable
's的参数,因此我知道在创建它时'soperator()
的预期函数签名。Callable
operator()
我试图在编译时执行两项检查,而它们可以在编译时完成。首先,我检查提供的字符串是否存在于预定义的字符串数组中。其次,我检查与该字符串关联的函数的签名是否与tuple
函数指针的预期签名匹配。我在编译时通过在查找函数throw()
的方法中创建“友好”错误消息。constexpr
我已经验证了通过创建一个constexpr
可调用对象,我在编译时收到了预期的错误消息。这行得通。如果我Dispatcher
直接使用 my ,则无法获得编译时消息,让调用站点将字符串转换为Callable
. 我知道当我使用运行时参数时,我的调度函数不会在constexpr
上下文中被调用——我故意没有创建那个函数constexpr
;关键是用运行时值调用它。但我认为隐式转换“发生在调用点”,而不是在被调用函数内。
因此,我认为在类似的调用中dispatcher("one", 1)
(调用参数为 1 的第一个函数)看起来像:“one”Callable
在 call-site 处转换为 a ,然后调用为dispatcher(Callable("one"), 1)
. 这意味着至少可以使用构造constexpr
函数。以我的经验,只要您不忽略调用的结果,就可以进行调用,否则就将其作为运行时进行。如果 result 被忽略,请参阅Constexpr 函数在编译时未调用。这不会发生——当转换发生在对我的调度函数的调用中时,转换构造函数会在运行时被调用!constexpr
constexpr
有谁知道我可以更改我的代码以在编译时调用转换构造函数的方法吗???我在这篇文章中找到了一个完全不同的解决方案来解决这类一般问题,但坦率地说,我更喜欢下面代码的语法,如果我能让它工作的话。
我不打算在这篇文章的正文中包含上述代码,而是包含一个更规范的示例来演示该行为,并展示我在上面引用的帖子中看到的行为,一体化。
下面的现场演示:https ://onlinegdb.com/r1s1OE77v
我的“真实”问题的现场演示,如果有兴趣:https ://onlinegdb.com/rJCQ2bGXw
首先是“测试夹具”:
// Modified from https://stackoverflow.com/a/40410624/12854372
// In a constexpr context, ContextIsConstexpr1(size_t) always
// simply sets _s to 1 successfully.
extern bool no_symbol_s_is_zero;
struct ContextIsConstexpr1 {
size_t _s;
constexpr ContextIsConstexpr1(size_t s) : _s(s ? 1 : no_symbol_s_is_zero) {}
};
// In a constexpr context, ContextIsConstexpr2(size_t) will cause
// a compile-time error if 0 is passed to the constructor
struct ContextIsConstexpr2 {
size_t _s;
constexpr ContextIsConstexpr2(size_t s) : _s(1) {
if(!s) {
throw logic_error("s is zero");
}
}
};
// Accept one of the above. By using a CONVERSION constructor
// and passing in a size_t parameter, it DOES make a difference.
ContextIsConstexpr1 foo(ContextIsConstexpr1 c) { return c; }
ContextIsConstexpr2 bar(ContextIsConstexpr2 c) { return c; }
现在测试代码:
int main()
{
constexpr size_t CONST = 1;
#define TEST_OBVIOUS_ONES false
// ------------------------------------------------------------
// Test 1: result is compile-time, param is compile-time
// ------------------------------------------------------------
#if TEST_OBVIOUS_ONES
// Compile-time link error iif s==0 w/ any optimization (duh)
constexpr auto test1_1 = ContextIsConstexpr1(CONST);
cout << test1_1._s << endl;
// Compile-time throw iif s==0 w/ any optimization (duh)
constexpr auto test1_2 = ContextIsConstexpr2(CONST);
cout << test1_2._s << endl;
#endif
// ------------------------------------------------------------
// Test 2: result is runtime, param is compile-time
// ------------------------------------------------------------
// Compile-time link error iif s==0 w/ any optimization ***See below***
auto test2_1 = ContextIsConstexpr1(CONST);
cout << test2_1._s << endl;
// Runtime throw iif s==0 w/ any optimization
// NOTE: Throw behavior is different than extern symbol behavior!!
auto test2_2 = ContextIsConstexpr2(CONST);
cout << test2_2._s << endl;
// ------------------------------------------------------------
// Test 3: Implicit conversion
// ------------------------------------------------------------
// Compile-time link error if (1) s==0 w/ any optimization *OR* (2) s>0 w/ low optimization!!
// Note: New s>0 error due to implicit conversion ***See above***
auto test3_1 = foo(CONST);
cout << test3_1._s << endl;
// Runtime throw iif s==0 w/ any optimization
auto test3_2 = bar(CONST);
cout << test3_2._s << endl;
// ------------------------------------------------------------
// Test 4: result is ignored, param is compile-time
// ------------------------------------------------------------
// Compile-time link error w/ any 's' iif low optimization
// Note: no error w/ s==0 with high optimization, new error w/ s>0 by ignoring result ***See above***
ContextIsConstexpr1{CONST};
// Runtime throw iif s==0 w/ any optimization
ContextIsConstexpr2{CONST};
// ------------------------------------------------------------
// Get runtime input, can't optimize this for-sure
// ------------------------------------------------------------
#if TEST_OBVIOUS_ONES
size_t runtime;
cout << "Enter a value: ";
cin >> runtime;
// ------------------------------------------------------------
// Test 5: result is runtime, param is runtime
// ------------------------------------------------------------
// Compile-time link error w/ any 's' w/ any optimization (duh)
auto test5_1 = ContextIsConstexpr1(runtime);
cout << test5_1._s << endl;
// Runtime throw iif s==0 w/ any optimization (duh)
auto test5_2 = ContextIsConstexpr2(runtime);
cout << test5_2._s << endl;
// ------------------------------------------------------------
// Test 6: result is ignored, param is runtime
// ------------------------------------------------------------
// Compile-time link error w/ any 's' w/ any optimization (duh)
ContextIsConstexpr1{runtime};
// Runtime throw iif s==0 w/ any 's' w/ any optimization (duh)
ContextIsConstexpr2{runtime};
#endif
}