-1

I would like somebody to clarify in which cases it is safe to use a static constexpr as a default argument to a class's constructor. To weed out exactly what is going on, consider the following code:

#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; } 

This code seems to compile, but does not link. Specifically, when compiling with GCC 6.3, I get

prog.cc:(.text+0x13): undefined reference to `Bar::default_options' collect2: error: ld returned 1 exit status 

However, if we comment out the offending line, then the code compiles, links, and runs correctly. So presumably there is no problem using a static constexpr size_t as a default argument:

#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; } 

Can somebody explain to me why the linkage works for a size_t, but not for an array of them?

I am aware that I could define the default options inline like this:

Bar( Options options = std::array<Option, 0>{}, Option option = default_option ){ std::cout << "Constructed with option " << option << std::endl; } 

I was just wondering if there was any other nicer fix so that the default options could easily be queried by anyone.

8
  • Are you building as C++14? Commented Mar 27, 2019 at 8:45
  • 1
    It won't. C++17 would. Just asked to add the appropriate tags. Commented Mar 27, 2019 at 8:47
  • 1
    GCC 6.3 doesn't support C++17 fully. In GCC 7.1 it works as intended wandbox.org/permlink/7T2gdl4uoayhJn1Z Commented Mar 27, 2019 at 8:49
  • 1
    As an aside, how did you get a permalink to Wandbox? I also was making the example there, but I never was able to figure out how to generate a permalink Commented Mar 27, 2019 at 8:54
  • 2
    After you hist "run" there appears a "share" option above the output panel. Hitting that gives you a "URL" anchor with the permalink and a twitter "share" option :) Commented Mar 27, 2019 at 8:55

1 Answer 1

2

As StoryTeller points out, the first code DOES compile and link with C++17 and GCC 7.1+. To get this to compile with C++11 and older versions of GCC, you need an out-of-class declaration of the array:

#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 & 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; } 
Sign up to request clarification or add additional context in comments.

4 Comments

Pre C++17 the line constexpr Bar::Options Bar::default_options; is actually the definition, not the declaration. It says to the compiler "I said that Bar had a member default_options; it belongs here". This isn't just pedantry; it means that the line cannot go in a header file that's included in multiple locations.
I am a little confused by that statement because in C++11, if I don't have the in-class initialization: default_options{};, that is, if I remove the trailing braces, then I get the error 'default_options' must have an initializer.
Perhaps a more lengthy answer would be appropriate. There seems to be a subtlety here that I am missing.
I can't make a longer writeup now, and I can't find anything particularly good and not jargon-dense to link to. The gist is that constexpr data members worked more like other static data members, which need to be declared in the class and defined outside the class. The declaration could have the initialization in some cases (and has to for constexpr so the compiler can actually use it as a constant expression), but you still need the out-of-class definition.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.