1

I have a bunch of template classes, based on an enumeration type. Here is the source code:

    #include <iostream>

// An enum type
    enum class ENUMR : unsigned char {
        SYSTEM1,
        SYSTEM2,
        UNKNOWN
    };

// template class; will later be specialized based on ENUMR enumerators
    template<ENUMR S>
    class System
    {};

// specialized System class, for enumerator SYSTEM1
    template<>
    class System<ENUMR::SYSTEM1>
    {
    private:
        static constexpr char identifier { 'G' };
    };

// An observation class recorded from a certain System instance
    template<ENUMR Sys,short int Freq>
    class Obs {
    public:
        Obs():
        m_system {System<Sys> {} }, // does not work
        m_frequency {Freq}
        {};
        //{
        //    m_system = System<Sys> {};  // works
        //}
    private:
        System<Sys> m_system;
        short int   m_frequency;
    };

// dummy code to test the template classes
    int main ()
    {

        System<ENUMR::SYSTEM1> s1;

        System<ENUMR::UNKNOWN> s2;

        System<ENUMR::SYSTEM1> s3 (System<ENUMR::SYSTEM1>);

        Obs<ENUMR::SYSTEM1, 1> obs;

        std::cout <<"\n";
        return 0;
    }

Initialization of the member (template) variable Obs::m_system produces a compile-time error, when performed as m_system {System<Sys> {} }. However, when the same variable is initialized as m_system = System<Sys> {}; the source codes gets compiled (the respective lines in the source code above are commented). Does that mean that the assignment operator works, but the copy constructor fails? If so, why? When compiled against the gcc version 4.8.3 i get the following error message:

test.cpp: In instantiation of ‘Obs<Sys, Freq>::Obs() [with ENUMR Sys = (ENUMR)0u; short int Freq = 1]’:
test.cpp:43:28:   required from here
test.cpp:25:22: error: too many initializers for ‘System<(ENUMR)0u>’
     m_frequency {Freq} {};
xnth
  • 156
  • 1
  • 8

2 Answers2

1

If you change

m_system{ System<Sys>{} },
m_frequency{ Freq }

to

m_system( System<Sys>{} ),
m_frequency( Freq )

it will compile. Note however, that the first initializer creates a default constructed temporary, and then uses the copy constructor to initialize the member. Since you want m_system to be default constructed, that initializer can be omitted.

  • Thanks a lot. It does compile, but why? Shouldn't m_system{ System{} } be the same as m_system( System{} ) in c++11 ? Unfortunately, i can't vote up your answer, i haven't got enough reputation ! – xnth Mar 30 '15 at 21:37
  • it seam to read it as an aggregate initializer, and since clang and gcc agree, i think i do too – Viktoria Andersson Mar 31 '15 at 19:13
0

System<ENUMR::SYSTEM1> is an aggregate, so the mem-initializer m_system { System<Sys>{} } performs aggregate-initialization. Since System<ENUMR::SYSTEM1> has no corresponding member - indeed it has no members at all - for the corresponding initializer System<Sys>{}, you get the error indicating there are too many initializers.

If System<ENUMR::SYSTEM1> were not an aggregate, the effect of that mem-initializer would be to direct-list-initialize m_system from a value-initialized temporary of the same type. Since the move/copy constructors are defaulted, you could achieve the equivalent effect for both the aggregate and non-aggregate case by directly value-initializing m_system with an empty brace initializer: m_system {}. (DEMO)

Community
  • 1
  • 1
Casey
  • 41,449
  • 7
  • 95
  • 125
  • Thanks a lot. Didn't even know what aggregate is! Sorry, i can't vote up your answer, i haven't got enough reputation ! – xnth Mar 30 '15 at 21:41