C++14: Инициализация переменных constexpr из значений параметров

Скажем, у меня есть класс, который может возвращать константное выражение через constexpr функция:

template<int N>
struct Foo {
  constexpr int Bar() const { return N; }
};

Если бы я хотел инициализировать значения constexpr из Foo::Bar()как передать параметр типа Foo? Я попробовал эти два, с примером constexpr переменная внутри каждого, чтобы проверить, что это может быть инициализировано:

template<int N>
constexpr int ByValue(Foo<N> f) {
  constexpr int i = f.Bar();
  return f.Bar();
}

template<int N>
constexpr int ByReference(const Foo<N> &f) {
  constexpr int i = f.Bar();
  return f.Bar();
}

constexpr int a = ByValue(Foo<1>{});
constexpr int b = ByReference(Foo<1>{});

Но Clang 3.7 вызывает ошибку на ByReference пока gcc >=5.1 не работает: живая демоверсия

main.cpp:15:25: error: constexpr variable 'i' must be initialized by a constant expression
      constexpr int i = f.Bar();
                        ^~~~~~~
main.cpp:22:25: note: in instantiation of function template specialization 'ByReference<1>' requested here
      constexpr int b = ByReference(Foo<1>{});

Какая разница между принятием const Foo & или равнина Foo, когда Bar является constexpr в любом случае и возвращает действительное константное выражение?

Что правильно и почему, GCC или Clang? Если возможно, ссылки на стандарт будут приветствоваться.

1 ответ

Решение

§5.20:

введите описание изображения здесь

Ссылка не имеет предшествующей инициализации с точки зрения iВпрочем: это параметр. Один раз инициализируется ByReference называется.

Давайте удалим constexpr от iдекларации и рассмотрим вызов ByReference в целом:

template<int N>
constexpr int ByReference(const Foo<N> &f) {
    int i = f.Bar();
    return i;
}

constexpr int j = ByReference(Foo<0>());

Это хорошо, так как f имеет предшествующую инициализацию. Инициализатор f также является константным выражением, поскольку неявно объявленный конструктор по умолчанию constexpr в этом случае (§12.1/5).
следовательно i инициализируется константным выражением, а вызов сам является константным выражением.

Другие вопросы по тегам