簡體   English   中英

C ++ 11 constexpr函數的參數在模板參數中傳遞

[英]C++11 constexpr function's argument passed in template argument

這曾經在幾周前工作:

template <typename T, T t>
T            tfunc()
{
    return t + 10;
}

template <typename T>
constexpr T       func(T t)
{
    return tfunc<T, t>();
}

int main()
{
    std::cout << func(10) << std::endl;
    return 0;
}

但是現在g++ -std=c++0x說:

main.cpp: In function ‘constexpr T func(T) [with T = int]’:
main.cpp:29:25:   instantiated from here
main.cpp:24:24: error: no matching function for call to ‘tfunc()’
main.cpp:24:24: note: candidate is:
main.cpp:16:14: note: template<class T, T t> T tfunc()
main.cpp:25:1: warning: control reaches end of non-void function [-Wreturn-type]

clang++ -std=c++11表示模板的tfunc<T, t>()被忽略,因為無效。

這是一個錯誤,還是修復?

PS:

g++ --version => g++ (GCC) 4.6.2 20120120 (prerelease)

clang++ --version => clang version 3.0 (tags/RELEASE_30/final) (3.0.1)

參數t不是常量表達式。 因此錯誤。 還應該指出,它不能是一個恆定的表達。

您可以將常量表達式作為參數傳遞,但在函數內部,保存該值的對象(參數)不是常量表達式。

由於t不是常量表達式,因此不能用作模板參數:

return tfunc<T, t>(); //the second argument must be a constant expression

也許,你想要這樣的東西:

template <typename T, T t>
T  tfunc()
{
    return t + 10;
}

template <typename T, T t>  //<---- t became template argument!
constexpr T  func()
{
    return tfunc<T, t>();
}

#define FUNC(a)  func<decltype(a),a>()

int main()
{
    std::cout << FUNC(10) << std::endl;
}

現在它應該工作: 在線演示

我覺得constexpr也必須在'運行時'上下文中有效,而不僅僅是在編譯時。 將函數標記為constexpr鼓勵編譯器嘗試在編譯時對其進行求值,但該函數仍必須具有有效的運行時實現。

實際上,這意味着編譯器不知道如何在運行時實現此函數:

template <typename T>
constexpr T       func(T t)
{
    return tfunc<T, t>();
}

解決方法是更改​​構造函數,使其將t參數作為普通參數,而不是模板參數,並將構造函數標記為constexpr

template <typename T>
constexpr T       tfunc(T t)
{
    return t + 10;
}
template <typename T>
constexpr T       func(T t)
{
    return tfunc<T>(t);
}

有三個級別的'constant-expression-ness':

  1. template int參數,或(非VLA)數組大小// 必須是常量表達式的東西
  2. constexpr // 可能是常量表達式的東西
  3. 非恆定表達

您無法將該列表中較低的項目轉換為該列表中較高的項目,但顯然可能是其他路徑。

例如,調用此函數

constexpr int foo(int x) { return x+1; }

不一定是常數表達式。

// g++-4.6 used in these few lines. ideone doesn't like this code. I don't know why
int array[foo(3)]; // this is OK
int c = getchar();
int array[foo(c)]; // this will not compile (without VLAs)

因此, 只有在編譯時執行所有參數和函數的實現時, constexpr函數的返回值才是常量表達式。

回顧一下問題:您有兩個函數,它們采用類型為T的參數。 一個將其參數作為模板參數,另一個作為“正常”參數。 我將調用兩個函數funcTfuncN而不是tfuncfunc 你希望能夠調用funcTfuncN 將后者標記為constexpr並沒有幫助。

任何標記為constexpr函數都必須是可編譯的,就好像constexpr不在那里一樣。 constexpr功能有點精神分裂。 在某些情況下,他們只能畢業於完全不變的表達。

不可能以簡單的方式實現funcN在運行時運行,因為它需要能夠為t的 所有可能值工作。 這將要求編譯器實例化tfunc許多實例,每個t的值為一個。 但如果您願意使用T的一小部分,則可以解決這個問題。模板遞歸限制為1024(以g ++為單位),因此您可以使用以下代碼輕松處理1024的T值:

#include<iostream>
#include<functional>
#include<array>
using namespace std;

template <typename T, T t>
constexpr T funcT() {
        return t + 10;
}

template<typename T, T u>
constexpr T worker (T t) {
        return t==0 ? funcT<T,u>() : worker<T, u+1>(t-1);

}
template<>
constexpr int worker<int,1000> (int ) {
            return -1;
}


template <typename T>
constexpr T       funcN(T t)
{
        return t<1000 ? worker<T,0>(t) : -1;
}

int main()
{
    std::cout << funcN(10) << std::endl;
    array<int, funcN(10)> a; // to verify that funcN(10) returns a constant-expression
    return 0;
}

它使用一個函數worker ,它將'normal'參數t遞歸轉換為模板參數u ,然后用它來實例化並執行tfunc<T,u>

關鍵的一行是return funcT<T,u>() : worker<T, u+1>(t-1);

這有局限性。 如果要使用long或其他整數類型,則必須添加另一個專門化。 顯然,此代碼僅適用於0到1000之間的t - 確切的上限可能與編譯器有關。 另一種選擇可能是使用排序的二進制搜索,對於每個2的冪,使用不同的工作函數:

template<typename T, T u>
constexpr T worker4096 (T t) {
        return t>=4096 ? worker2048<T, u+4096>(t-4096) : worker2048<T, u>(t);

}

我認為這將解決該模板遞歸限制,但它仍然需要非常大量的實例,並會使編譯速度慢,如果在所有工作。

看起來它應該給出一個錯誤 - 它無法知道你傳入一個常量值作為t到func。

更一般地,您不能將運行時值用作模板參數。 模板本身就是一個編譯時構造。

暫無
暫無

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

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