Подразумевает ли constexpr встроенный?

105

Рассмотрим следующую встроенную функцию:

// Inline specifier version
#include<iostream>
#include<cstdlib>

inline int f(const int x);

inline int f(const int x)
{
    return 2*x;
}

int main(int argc, char* argv[])
{
    return f(std::atoi(argv[1]));
}

и версия, эквивалентная constexpr:

// Constexpr specifier version
#include<iostream>
#include<cstdlib>

constexpr int f(const int x);

constexpr int f(const int x)
{
    return 2*x;
}

int main(int argc, char* argv[])
{
    return f(std::atoi(argv[1]));
}

Мой вопрос: constexprподразумевает ли inlineспецификатор спецификатор в том смысле, что если в constexprфункцию передается непостоянный аргумент , компилятор попытается выполнить inlineэту функцию, как если бы inlineспецификатор был помещен в ее объявление?

Гарантирует ли это стандарт C ++ 11?

Винсент
источник
5
«[Будет ли] компилятор попытаться встроить функцию» - это не то, что inlineделает спецификатор. (Или, может быть, я неправильно понял вашу формулировку.)
Люк Дантон
5
Спецификатор inlineбольше не имеет ничего общего с встраиванием
K-
2
Вопрос возникает из-за неправильного предположения, inlineкоторое напрямую связано с встраиванием. Итак, нет, constexprспецификатор не подразумевает inlineспецификатор в этом смысле, поскольку этого смысла не существует.
Christian Rau

Ответы:

139

Да ([dcl.constexpr], §7.1.5 / 2 в стандарте C ++ 11): «функции constexpr и конструкторы constexpr неявно встроены (7.1.2)».

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

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

Джерри Гроб
источник
Учитывая, что идея состоит в том, что константные выражения оцениваются во время компиляции, я полагаю, что большинство случаев использования constexprфункций вообще не вызовет генерации кода ...
Керрек С.Б.
11
constexprФункции @KerrekSB потенциально оцениваются во время компиляции. Однако стандарт C ++ 14 изобилует стандартами, которые, скорее всего, будут вызываться во время выполнения. Например:std::array<T,N>::at
Eponymous
@Eponymous да, но в качестве кодов операций останется только самая сокращенная форма. например: связанные проверки будут оцениваться во время сборки, поскольку их кодовый путь является константным. Но возвращаемое значение будет * (данные + смещение)
v.oddou
16

constexprне подразумевает inlineдля нестатических переменных (встроенные переменные C ++ 17)

Хотя constexprэто и подразумевается inlineдля функций, он не имеет такого эффекта для нестатических переменных, учитывая встроенные переменные C ++ 17.

Например, возьмем минимальный пример, который я разместил по адресу: Как работают встроенные переменные? и удалите inline, оставив только constexpr, тогда переменная получит несколько адресов, чего в основном избегают встроенные переменные.

constexpr однако статические переменные неявно статичны.

Минимальный пример, который constexprподразумевает inlineдля функций

Как упоминалось по адресу: https://stackoverflow.com/a/14391320/895245, основной эффект inlineзаключается не во встроении, а в разрешении нескольких определений функции, стандартная цитата по адресу: Как файл заголовка C ++ может включать реализацию?

Мы можем убедиться в этом, поиграв со следующим примером:

main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    assert(shared_func() == notmain_func());
}

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

inline int shared_func() { return 42; }
int notmain_func();

#endif

notmain.cpp

#include "notmain.hpp"

int notmain_func() {
    return shared_func();
}

Скомпилируйте и запустите:

g++ -c -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'notmain.o' 'notmain.cpp' 
g++ -c -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'main.o' 'main.cpp' 
g++ -ggdb3  -O0 -Wall -Wextra -std=c++11 -pedantic-errors  -o 'main.out' notmain.o main.o
./main.out

Если мы удалим inlineиз shared_func, ссылка не удастся:

multiple definition of `shared_func()'

потому что заголовок включается в несколько .cppфайлов.

Но если мы заменим inlineна constexpr, то он снова работает, потому что constexprтакже подразумевает inline.

GCC реализует это, помечая символы как слабые в объектных файлах ELF: Каким образом файл заголовка C ++ может включать реализацию?

Протестировано в GCC 8.3.0.

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
3
Кстати, объявленная статическая переменная-член класса constexprвсе еще встроена. cppreference.com : объявленная статическая переменная-член (но не переменная области пространства имен) constexprнеявно является встроенной переменной.
anton_rh 03
@anton_rh спасибо, я не видел этого правила, обновите ответ.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
это не то, что говорит open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0386r0.pdf . он говорит, что constexpr подразумевает встроенные переменные. без упоминания разницы между областью пространства имен и областью класса.
v.oddou