Вызов не constexpr из функции шаблона constexpr

я наткнулся на функции шаблона constexpr, вызывающие не функции constexpr: В следующем фрагменте бар не удается скомпилировать, как ожидалось, из-за вызова non constexpr set но фу компилирует. Может кто-нибудь сказать мне, почему фу компилирует?

template<class T>
void set(T& x){
    x++;
}

template<class T>
constexpr void foo(T& x){
    set<T>(x);
}

constexpr void bar(int& x){
    set<int>(x);
}

void bar(){
    int x = 5;
    foo(x);
    bar(x);
}

компилятор не компилируется с ошибкой:

<source>: In function 'constexpr void bar(int&)':
<source>:12:13: error: call to non-constexpr function 'void set(T&) [with T = int]'
     set<int>(x);
     ~~~~~~~~^~~
Compiler returned: 1

Edit: добавлена ошибка компилятора и перефразированный вопрос. Побочные эффекты не в фокусе здесь.

как указано Боловым и Rekete1111 ниже шаблон будет оценен позже. Когда ограничения constexpr не выполняются, функция шаблона constexpr становится своего рода полу-функцией constexpr. Результат компиляции-Og следующего фрагмента кода показывает, что constexpr фу будет оптимизирован и общий foo2 нет, пока оба делают не выполнить требования функций constexpr (возможно, следствие inline импликация constexpr):

template<class T>
void set(volatile T& x){
    x++;
}

template<class T>
constexpr void foo(T& x){
    set<T>(x);
}

template<class T>
void foo2(T& x){
    set<T>(x);
}

void bar(){
    int x = 5;
    foo(x);
    foo2(x);
}

результат компиляции:

void set<int>(int volatile&):
  ldr r3, [r0]
  add r3, r3, #1
  str r3, [r0]
  bx lr
void foo2<int>(int&):
  push {r4, lr}
  bl void set<int>(int volatile&)
  pop {r4, lr}
  bx lr
bar():
  push {r4, lr}
  sub sp, sp, #8
  add r4, sp, #8
  mov r3, #5
  str r3, [r4, #-4]!
  mov r0, r4
  bl void set<int>(int volatile&)
  mov r0, r4
  bl void foo2<int>(int&)
  add sp, sp, #8
  pop {r4, lr}
  bx lr

1 ответов


потому что foo - это шаблон функции, а bar - это функция.

функции (например,bar) чтобы быть constexpr, он должен соответствовать всем правилам constexpr (которые меняются от стандарта к стандарту), и это проверяется при определении функции. Вы получаете ошибку, если эти правила не выполняются.

для шаблона функции, поскольку у вас есть только шаблон для создания функций, вы не можете применять правила для constexpr. Е. Г. в вашем примере в точке определения шаблона вы не знаете, если set<T>(x) is constexpr потому что у вас могут быть некоторые экземпляры шаблонов set кто constexpr и некоторые другие экземпляры шаблонов для set какие нет. Так что вы не можете проверить это foo соответствует требованиям constexpr. Вы можете проверить только конкретные экземпляры foo если constexpr например foo<int> или foo<char> etc.

C++ обрабатывает эту ситуацию, позволяя constexpr для шаблона функции без разбора (вроде). Однако если экземпляры шаблона не отвечают требованиям для constexpr, то это разрешено, но специализация не разрешена в постоянном выражении.

вы можете увидеть это с немного измененным кодом из вашего примера:

auto set(int a) { return a; }
constexpr auto set(char a) { return a; }

template<class T>
constexpr auto foo(T x){
    return set(x);
}

auto test()
{
    auto x = foo(24); // foo<int>  OK, no error
    //constexpr auto cx = foo(24) // foo<int> compiler error

    auto y = foo('a'); // foo<char> OK, no erro
    constexpr auto y = foo('a'); // foo<char> OK
}

§7.1.5 [dcl.функция constexpr]

  1. если созданная специализация шаблона шаблона функции constexpr или функции-члена класса шаблон не будет удовлетворять требования к функции constexpr или конструктору constexpr, эта специализация по-прежнему является функцией constexpr или constexpr конструктор, даже если вызов такой функции может появиться в константное выражение. Если никакая специализация шаблона не будет удовлетворяют требованиям для функции пользователем или пользователем конструктор, если он рассматривается как функция или конструктор без шаблона, шаблон плохо сформирован; нет диагностики требуемый.