c++ Unexplainable class “ has not been declared” error due to namespace
Asked Answered
P

1

6

I have some template class that has two private static members. Users define a traits struct and provide it to the template class, which then derives from it.

Then in a c++ file the user define the static members, with one member initialized from the other. For some reason I get a "class has not been declared" error if I dont fully specify the namespace for the arg. This is only an issue when I'm in a nested namespace, there is no issue if you define the type in a single top level namespace, which makes me think this is a compiler bug. Trimmed down example below, compiling with gcc 7.2

template<typename Traits>
struct Base 
{
    static int x;
    static int y;
};

namespace foo::bar
{
    struct BarTraits
    {
    };

    using Bar = Base<BarTraits>;

    template<> int Bar::x = 0;
    template<> int Bar::y( Bar::x );  //error 
    //template<> int Bar::y( foo::bar::Bar::x ); //no error
}
Pulsar answered 13/2, 2018 at 18:10 Comment(6)
Beware of writing questions in a way that presupposes that your question is not answerable. If your error were truly unexplainable, then there wouldn't be much point in asking about it here.Overelaborate
You should define them in the same namespace Base is defined in. Or actually borrow them from Traits template parameter directly.Jacobba
Other compilers give an error already at x, saying cannot define or redeclare 'x' here because namespace 'bar' does not enclose namespace 'Base<foo::bar::BarTraits>'Jolyn
Never confess that something "makes me think this is a compiler bug."Amersfoort
template<> int Bar::y( Bar::x ); doesn't define a static data member with an initializer. You can't use round parentheses for an initializer here.Achromatic
VTT - ok I can see that, but then why doesn't int Bar::y complain without fully specifying it?Pulsar
W
0

According to C++ standard temp.expl.spec#3

An explicit specialization may be declared in any scope in which the corresponding primary template may be defined.

Your code violates this statement, because Bar::x and Bar::y are specialized in namespace foo::bar.

GCC incorrectly accepts first two specializations because of known defect https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56119

The following fixed code

template<typename Traits>
struct Base  {
    static int x;
    static int y;
};

struct BarTraits {};
using Bar = Base<BarTraits>;

template<> int Bar::x = 0;
template<> int Bar::y( Bar::x );

is accepted by GCC, Clang, MSVC: https://gcc.godbolt.org/z/MPxjTzbah

Watthour answered 27/11, 2021 at 8:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.