#c #c 11
#c #c 11
Вопрос:
Я хотел бы, чтобы кто-нибудь разъяснил, в каких случаях безопасно использовать static constexpr
в качестве аргумента по умолчанию для конструктора класса. Чтобы точно определить, что происходит, рассмотрим следующий код:
#include <array>
#include <iostream>
struct Bar {
using Option = size_t;
using Options = std::array<Option, 0>;
static constexpr Option default_option = 8080;
static constexpr Options default_options{};
//template <typename OptionT = Options>
Bar(
Options options = default_options,
Option option = default_option
){
std::cout << "Constructed with option " << option << std::endl;
}
};
int main() {
Bar bar;
}
Этот код, похоже, компилируется, но не связывается. В частности, при компиляции с GCC 6.3 я получаю
prog.cc:(.text 0x13): undefined reference to `Bar::default_options'
collect2: error: ld returned 1 exit status
Однако, если мы закомментируем строку-нарушитель, тогда код компилируется, связывается и выполняется правильно. Таким образом, предположительно, нет проблем с использованием static constexpr size_t
в качестве аргумента по умолчанию:
#include <array>
#include <iostream>
struct Bar {
using Option = size_t;
using Options = std::array<Option, 0>;
static constexpr Option default_option = 8080;
static constexpr Options default_options{};
//template <typename OptionT = Options>
Bar(
//Options options = default_options,
Option option = default_option
){
std::cout << "Constructed with option " << option << std::endl;
}
};
int main() {
Bar bar;
}
Может ли кто-нибудь объяснить мне, почему привязка работает для size_t
, но не для array
любого из них?
Я знаю, что я мог бы определить встроенные параметры по умолчанию следующим образом:
Bar(
Options options = std::array<Option, 0>{},
Option option = default_option
){
std::cout << "Constructed with option " << option << std::endl;
}
Мне просто интересно, есть ли какое-либо другое более приятное исправление, чтобы параметры по умолчанию могли легко запрашиваться кем угодно.
Комментарии:
1. Вы создаете как C 14?
2. Этого не произойдет. C 17 бы. Просто попросили добавить соответствующие теги.
3. GCC 6.3 не поддерживает C 17 полностью. В GCC 7.1 это работает так, как задумано wandbox.org/permlink/7T2gdl4uoayhJn1Z
4. Кстати, как вы получили постоянную ссылку на Wandbox? Я также приводил там пример, но мне так и не удалось выяснить, как сгенерировать постоянную ссылку
5. После того, как вы нажмете «выполнить», над панелью вывода появится опция «поделиться». Нажатие на это дает вам привязку «URL» с постоянной ссылкой и опцию «поделиться» в Twitter 🙂
Ответ №1:
Как указывает StoryTeller, первый код ДЕЙСТВИТЕЛЬНО компилируется и связывается с C 17 и GCC 7.1 . Чтобы заставить это скомпилироваться с C 11 и более старыми версиями GCC, вам необходимо внеклассовое объявление массива:
#include <array>
#include <iostream>
struct Bar {
using Option = size_t;
using Options = std::array<Option, 0>;
static constexpr Option default_option = 8080;
static constexpr Options default_options{};
//template <typename OptionT = Options>
Bar(
Options options = default_options,
Option option = default_option
){
std::cout << "Constructed with option " << option << std::endl;
std::cout << "Constructed with options..." << std::endl;
for (auto amp; other_option : options)
std::cout << other_option << ", ";
std::cout << std::endl;
}
};
// !!!! Needed for C 11 and lower gcc<7.1 versions
constexpr Bar::Options Bar::default_options;
int main() {
Bar bar;
}
Комментарии:
1. До C 17 строка
constexpr Bar::Options Bar::default_options;
фактически была определением , а не объявлением . Компилятору сообщается: «Я сказал, что уBar
него есть членdefault_options
; ему место здесь». Это не просто педантизм; это означает, что строка не может входить в файл заголовка, который включен в несколько расположений.2. Меня немного смущает это утверждение, потому что в C 11, если у меня нет инициализации в классе:
default_options{};
, то есть если я удаляю завершающие фигурные скобки, я получаю ошибку'default_options' must have an initializer
.3. Возможно, более длинный ответ был бы уместен. Кажется, здесь есть тонкость, которую я упускаю.
4. Я не могу сейчас сделать более длинную запись, и я не могу найти ничего особенно хорошего и не перегруженного жаргоном, на что можно было бы ссылаться. Суть в том, что
constexpr
элементы данных работают больше как другие статические элементы данных, которые должны быть объявлены в классе и определены вне класса. В некоторых случаях объявление может иметь инициализацию (и должно иметь дляconstexpr
, чтобы компилятор мог фактически использовать его как постоянное выражение), но вам все равно нужно внеклассовое определение.