簡體   English   中英

constexpr 構造函數不被稱為隱式類型轉換的 constexpr

[英]constexpr constructor not called as constexpr for implicit type conversion

我編寫了一些代碼,這些代碼能夠根據調用站點提供與給定函數關聯的字符串(通過函數指針tuple和並行數組)調度到函數。 調度函數不是直接接受字符串,而是接受Callable類型,其中const char*可轉換為Callable

Callable的構造函數是constexpr ,並使用基本的遞歸搜索從注釋的tuple查找函數。 我已經驗證構造函數能夠正常工作並創建一個constexpr Callable (包括示例)。 由於調度函數接收的參數傳遞給Callableoperator()我知道預期的函數簽名Callableoperator()在我創建的時間。

我試圖在編譯時執行兩項檢查,當它們可以在編譯時完成時。 首先,我檢查提供的字符串是否存在於預定義的字符串數組中。 其次,我檢查與該字符串關聯的函數簽名是否與函數指針tuple中的預期簽名匹配。 我在編譯時通過throw()在查找函數的constexpr方法中創建“友好”錯誤消息。

我已經驗證,通過創建可調用的constexpr ,我在編譯時收到了預期的錯誤消息。 這有效。 如果我直接使用我的Dispatcher ,讓調用站點將字符串轉換為Callable ,那么不起作用的是獲取編譯時消息。 我知道當我使用運行時參數時,我的 dispatch 函數不會在constexpr上下文中被調用——我故意沒有讓這個函數constexpr 關鍵是用運行時值調用它。 我認為隱式轉換“發生在調用站點” ,而不是在被調用函數內。

因此,我認為在像dispatcher("one", 1)這樣的調用中(它調用參數為 1 的第一個函數)看起來像: "one"在 call-site被轉換為Callable ,然后調用被制作為dispatcher(Callable("one"), 1) 這將意味着constexpr構造函數可以使用,至少。 根據我的經驗,只要您不忽略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
}

有誰知道我可以更改我的代碼以在編譯時調用轉換構造函數的方法,如果可以的話

正如我在鏈接發布中所說,編譯時對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

演示

但並不比提議的dispatcher.dispatch(MAKE_CHAR_SEQ("a"), 1);更好 (或擴展dispatcher.dispatch("a"_cs, 1); )(提供調度重載以能夠創建constexpr Callable )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM