c++ - constexpr 构造函数不被称为隐式类型转换的 constexpr

标签 c++ string runtime constexpr constexpr-function

我编写了一些代码,它能够根据调用站点提供与给定函数关联的字符串(通过函数指针和并行数组的tuple)来分派(dispatch)给函数。 dispatch 函数不直接接受字符串,而是接受 Callable类型,其中一个 const char*可转换为 Callable .Callable 的构造函数是 constexpr , 并从注解的 tuple 中查找函数使用基本的递归搜索。我已验证构造函数能够正常工作并创建 constexpr Callable (包括示例)。由于 dispatch 函数接收到要传递给 Callable 的参数。的operator() ,我知道 Callable 的预期函数签名的operator()在我创建它的时候。
我试图在编译时执行两项检查,而它们可以在编译时完成。首先,我检查提供的字符串是否存在于预定义的字符串数组中。其次,我检查与该字符串关联的函数的签名是否与 tuple 中的预期签名匹配。的函数指针。我在编译时通过 throw() 创建“友好”错误消息在 constexpr 内查找函数的方法。
我已经通过创建 constexpr 验证了这一点可调用,我在编译时收到预期的错误消息。这行得通。如果我使用我的 Dispatcher,则无法获得编译时消息。直接,让调用站点将字符串转换为 Callable .我知道当我使用运行时参数时,我的调度函数不会在 constexpr 中调用。上下文 - 我故意没有做这个功能 constexpr ;关键是用运行时值调用它。但是我认为隐式转换“发生在调用站点” ,不在被调用函数内。
因此,我认为在像 dispatcher("one", 1) 这样的电话中(它调用参数为 1 的第一个函数)看起来像:“one”被转换为 Callable 在调用站点 ,然后调用 dispatcher(Callable("one"), 1) .这意味着 constexpr构造函数 可以至少可以使用。根据我的经验,只要你不忽略 constexpr 的结果调用,调用为 constexpr如果可以,则将其作为运行时。见 Constexpr functions not called at compile-time if result is ignored .这不会发生——转换构造函数在运行时被调用 当转换发生在对我的调度函数的调用中时!
有谁知道我可以更改我的代码以在编译时调用转换构造函数的方法吗???我在 this post 中找到了一个完全不同的解决方案来解决此类问题。 ,但坦率地说,我更喜欢下面代码的语法,如果我能让它工作的话。
我不打算在这篇文章的正文中包含上述代码,而是包含一个更规范的示例来演示该行为,并展示我在上面引用的帖子中看到的行为,一体化。
以下内容的现场演示: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
}

最佳答案

Does anyone know of a way I can change my code to get the conversion constructor to be called at compile-time if it can be


正如我在链接帖子中所说,调用 constexpr编译时的函数已完成 仅限 在不断的表达。
参数不是 constexpr。
一种解决方法是使用 MACRO:
#define APPLY_DISPATCHER(dispatcher, str, ...) \
    do { \
        constexpr callable_type_t<decltype(dispatcher),  decltype(make_tuple(__VA_ARGS__))> callable(str); \
        (dispatcher)(callable, __VA_ARGS__); \
    } while (0)
template <typename Dispatcher, typename Tuple> struct callable_type;

template <typename Dispatcher, typename ... Ts>
struct callable_type<Dispatcher, std::tuple<Ts...>>
{
    using type = typename Dispatcher::template Callable<Ts...>;
};

template <typename Dispatcher, typename Tuple> 
using callable_type_t = typename callable_type<Dispatcher, Tuple>::type;
随着用法:
APPLY_DISPATCHER(dispatcher, "one", 1);
APPLY_DISPATCHER(dispatcher, "a", 1); // Fail at compile time as expected
Demo .
但并不比建议的更好dispatcher.dispatch(MAKE_CHAR_SEQ("a"), 1); (或带有扩展名 dispatcher.dispatch("a"_cs, 1); )(提供调度重载以能够创建 constexpr Callable )。

关于c++ - constexpr 构造函数不被称为隐式类型转换的 constexpr,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63571902/

相关文章:

java - 如何检查字符串是否包含搜索项

java - 在运行时重写现有对象的方法的任何简单方法

c++ - OpenCV图像变换和透视变化

c++ - 共享指针和原始指针生命周期

c++ - 如何在 rxcpp 中处理请求/响应流

c# - C#中如何对 float 进行四舍五入

javascript - 正则表达式删除/之前和之后的3个字符

运行时的 Java 编译器

java - 创建.java文件并在运行时将其编译为.class文件

c++ - 如何实现打印功能?