簡體   English   中英

靜態斷言 std::array 的大小,其類型是使用 decltype 從成員函數的返回值獲得的

[英]Statically asserting the size of a std::array whose type is obtained using decltype from the return value of a member function

(抱歉標題笨拙;我不知道如何更簡潔地總結這個問題。如果有人有更好的想法,請隨時編輯!)

我想寫一個自由函數,它可以根據類的成員函數的返回值自動確定其參數的類型。 使用decltype ,這部分很容易。

希望有一個編譯時斷言來驗證對該參數類型所做的假設,這就是我提出的解決方案失敗的地方。

考慮以下 MCVE:

#include <type_traits>
#include <array>
#include <iostream>

class Foo
{
public:
   std::array<int, 10> Get();
};

void PrintFoos(const decltype(Foo().Get())& param)
{
    static_assert(param.size() == 10, "wrong size");
    for (const auto& i : param)
    {
        std::cout << i << "\n";
    }
}

GCC 很好地編譯了上面的代碼,沒有警告。

另一方面,Clang 抱怨道:

error: static_assert expression is not an integral constant expression
    static_assert(param.size() == 10, "wrong size");
                  ^~~~~~~~~~~~~~~~~~

MSVC 也是如此:

(13): error C2131: expression did not evaluate to a constant
(13): note: failure was caused by a read of a variable outside its lifetime
(13): note: see usage of 'param'

當其他編譯器拒絕它時,為什么 GCC 編譯得很好? 是否有一個 GCC 擴展可以讓我從中受益以支持這一點?

語言標准對此有何看法? 我的目標是 C++17,但也有興趣知道 C++14 是否有任何變化。

額外問題:有沒有辦法可以修改此代碼以使其工作? 顯然,如果decltype表達式不計算為std::array類型,則static_assert應該失敗,因為size()成員函數不會是constexpr 我想有一個解決方案涉及添加模板輔助函數,但除非絕對必要,否則我寧願不添加另一個函數定義。

我相信 clang 和其他人(icc 和 MSVC)在這里在技術上是正確的,而 GCC 是錯誤的。 static_assert 聲明采用常量表達式[expr.const]/2 我相信手頭案例的相關 C++17 措辭應該是[expr.const]/2.11

表達式e核心常量表達式,除非e的計算遵循抽象機的規則,將計算以下表達式之一:

  • […]
  • 引用變量或引用類型數據成員的id 表達式,除非該引用具有前面的初始化並且
    • 它用常量表達式初始化或
    • 它的生命周期從e的求值開始;
  • […]

上面static_assert的表達式顯然就是這樣做的,但是( param是一個id 表達式,它指的是引用類型的變量,沒有任何例外情況適用)。 因此,它不是一個常量表達式,並且程序格式錯誤 [dcl.dcl]/6 C++14 標准中的相關措辭似乎是相同的。 我會認為這是 GCC 中的一個錯誤。

如果您可以將函數更改為模板,則可以簡單地推斷出大小:

template <int N>
void PrintFoos(const std::array<int, N>& param)
{
    …
}

或者,如果你想讓一切都依賴於Foo ,你也可以定義一個公共常量並從中派生數組類型等:

class Foo
{
public:
    static constexpr auto size = 10;
    std::array<int, size> Get();
};

void PrintFoos(const decltype(Foo().Get())& param)
{
    static_assert(Foo::size == 10, "wrong size");
}

而且,當然,您可以使用輔助模板:

template <typename T>
constexpr std::size_t deduce_array_size = 0U;

template <typename T, std::size_t N>
constexpr std::size_t deduce_array_size<std::array<T, N>> = N;

template <typename T>
constexpr std::size_t deduce_array_size<T&> = deduce_array_size<T>;

template <typename T>
constexpr std::size_t deduce_array_size<T&&> = deduce_array_size<T>;

進而

void PrintFoos(const decltype(Foo().Get())& param)
{
    static_assert(deduce_array_size<decltype(param)> == 10, "wrong size");
}

最后,另一種選擇(受 Yakk - Adam Nevraumont 下面的評論啟發)是簡單地在常量表達式中創建數組類型的純右值並詢問其大小:

static_assert(std::decay_t<decltype(param)>{}.size() == 10, "wrong size");

暫無
暫無

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

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