Consider the following code:
template<int X, int Y>
struct S
{
typedef int func(int,float) const;
};
template<int X>
struct D : public S<X,6>
{
typename S<X,6>::func func;
};
template<int X>
int D<X>::func(int,float) const
{
return 1;
}
//----------------
struct W : public S<7,8>
{
S<7,8>::func func;
};
int W::func(int,float) const
{
return 2;
}
#include <iostream>
int main()
{
W w;
std::cout << w.func(1,4.3) << "\n";
D<3> d;
std::cout << d.func(1,4.3) << "\n";
}
If I comment out the code declaring class D and D::func() as well as the corresponding lines in main(), the code compiles normally, and I see 2 in output, as expected.
But when I make the derived class template (adding typename before function declaration, as S<X.6> is a dependent scope), I get the following errors:
test.cpp:13:27: error: no ‘int D<X>::func(int, float) const’ member function declared in class ‘D<X>’
int D<X>::func(int,float) const
^
test.cpp: In instantiation of ‘struct D<3>’:
test.cpp:32:10: required from here
test.cpp:10:27: error: field ‘D<3>::func’ invalidly declared function type
typename S<X,6>::func func;
^
Why can't I declare func in a template derived class, while in non-template class it's OK?
What exactly is "invalidly declared function type"? what is invalid here?
N3337 [dcl.fct]/10: A typedef of function type may be used to declare a function but shall not be used to define a function.
By this rule, both D and W are technically well-formed. I think the reason this doesn't compile with GCC or Clang is that declaring a function with a typedef is really rare. Declaring a function with a member typedef dependent on a template parameter is even rarer. Looks like you've just hit on a dark corner where compiler support is limited.
Funnily enough, MSVC actually does the right thing here.
Your best option is probably to find a different way to express your classes. I can't really give any directed advice without knowing more about the problem you are trying to solve, but if you ask a new question with details we can give some suggestions.
You might also think about filing a bug report for GCC and Clang.
Edit:
However, as Jarod42 pointed out, the dependent type could later be defined as something other than a function type, making the declaration invalid. The reason MSVC works where GCC and Clang does not is the same reason MSVC doesn't require typename in some places: it doesn't correctly implement two-phase lookup. Having this case fully specified would require something like function_typename to mark a dependent name as being a function type. I think declaring a function based on a dependent type is underspecified and inconsistent as a result of this being a very rare case.
As the error message states : In D func is not a member function, thus you cannot provide a definition for it. It is a member with type S<X,6>::func.
Related
When I compile the following snippet with g++
template<class T>
class A
{};
template<class T>
class B
{
public:
typedef A<T> A;
};
the compiler tells me
error: declaration of ‘typedef class A<T> B<T>::A’
error: changes meaning of ‘A’ from ‘class A<T>’
On the other hand, if I change the typedef to
typedef ::A<T> A;
everything compiles fine with g++. Clang++ 3.1 doesn't care either way.
Why is this happening? And is the second behavior standard?
g++ is correct and conforming to the standard. From [3.3.7/1]:
A name N used in a class S shall refer to the same declaration in its
context and when re-evaluated in the completed scope of S. No
diagnostic is required for a violation of this rule.
Before the typedef, A referred to the ::A, however by using the typedef, you now make A refer to the typedef which is prohibited. However, since no diagnostic is required, clang is also standard conforming.
jogojapan's comment explains the reason for this rule.
Take the following change to your code:
template<class T>
class A
{};
template<class T>
class B
{
public:
A a; // <-- What "A" is this referring to?
typedef A<T> A;
};
Because of how class scope works, A a; becomes ambiguous.
I will add to Jesse's answer about the seemingly peculiar behavior of GCC in compiling:
typedef A<T> A;
vs
typedef ::A<T> A;
This also applies to using statements as well of the form:
using A = A<T>;
using A = ::A<T>;
What seems to be happening within GCC, is that during the compilation of the typedef/using statement declaring B::A, that the symbol B::A becomes a valid candidate within the using statement itself. I.e. when saying using A = A<T>; or typedef A<T> A; GCC considers both ::A and B::A valid candidates for A<T>.
This seems odd behavior because as your question implies, you don't expect the new alias A to become a valid candidate within the typedef itself, but as Jesse's answer also says, anything declared within a class becomes visible to everything else inside the class - and in this case apparently even the declaration itself. This type of behavior may be implemented this way to permit recursive type definitions.
The solution as you found is to specify for GCC precisely which A you're referring to within the typedef and then it no longer complains.
In the following code A::Type is private in A.
class A {
typedef int Type;
};
void func(int t, A::Type var)
{
return;
}
Trying to compile with gcc gives the following error.
test.cpp: In function 'void func(int, A::Type)':
test.cpp:12:21: error: 'typedef int A::Type' is private within this context
void func(int t, A::Type var)
^~~~
test.cpp:2:17: note: declared private here
typedef int Type;
^~~~
But if I change the first parameter to be a template parameter, like so
template<typename T>
void func(T t, A::Type var)
{
return;
}
the access specifier of Type seems to be ignored and this compiles.
At first I suspected it might be a bug in gcc, but MSVC 2015 exhibits the same behaviour.
Is this behaviour for some reason required by the C++ standard? If so what is the rationale here?
At first I suspected it might be a bug in gcc,
You suspected correctly. GCC has multiple open bugs about missing accessibility checks in templates, but it is clear that its developers consider those bugs that need to be fixed. Your example is probably covered by template function specialization: does not respect access specifier, even though that uses a dependent typedef, whereas yours is non-dependent.
but MSVC 2015 exhibits the same behaviour.
Nobody is going to doubt you if you say MSVC has bugs affecting conformance. :)
Edit: I feel kind of silly now. The posted code actually works with ???=decltype(acc(base(i)))... The error was something else - highly confusing though. I will post a new question about that. Thank you for your help.
I have some code like this:
template<typename Base, typename Acc>
struct Foo
{
Base base;
Acc acc;
auto operator()(unsigned i) const -> decltype(???) // what is ???
{ return acc(base(i)); }
};
How do I get the correct return type?
I tried with decltype(acc(base(i))) but this gives an error (using GCC 4.6.3):
there are no arguments to ‘base’ that depend on a template parameter,
so a declaration of ‘base’ must be available
I have been troubled for hours or days around such problems. Typically gcc wants this-> but clang does not. In some cases Foo:: also helps but there have been cases where I have given up using the member names. The following is only using the type names and (though more verbose) I think will not have any problems with either:
template<typename Base, typename Acc>
struct Foo
{
Base base;
Acc acc;
template<typename S>
using result = typename std::result_of<S>::type;
auto operator()(unsigned i) const
-> result<const Acc(result<const Base(unsigned)>)>
{ return acc(base(i)); }
};
More generally, whenever decltype(member_name) is causing problems, you can safely use decltype(std::declval<member_type>()) where member_type is const-qualified if the member function using it is const.
Even more generally, if you have a member function that is &&, & or const& qualified, use member_type, member_type& or const member_type& respectively in such expressions.
I have never looked into what the standard says but in practice compilers treat expressions differently in trailing return types and inside function definitions when it comes to class members.
Yesterday I ran into a g++ (3.4.6) compiler problem for code that I have been compiling without a problem using the Intel (9.0) compiler. Here's a code snippet that shows what happened:
template<typename A, typename B>
class Foo { };
struct Bar {
void method ( Foo<int,int> const& stuff = Foo<int,int>() );
};
The g++ compiler error is:
foo.cpp:5: error: expected `,' or `...' before '>' token
foo.cpp:5: error: wrong number of template arguments (1, should be 2)
foo.cpp:2: error: provided for `template<class A, class B> struct Foo'
foo.cpp:5: error: default argument missing for parameter 2 of `void Bar::method(const Foo<int, int>&, int)'
Apparently, the default argument is not accepted when written this way, and the compiler assumes that instead of the second template argument a new function argument is specified, for which it then expects a default value because the stuff argument has one. I can help the compiler by creating a typedef, and then everything compiles fine:
template<typename A, typename B>
class Foo { };
struct Bar {
typedef Foo<int,int> FooType;
void method ( FooType const& stuff = FooType() );
};
So I can solve my problem, but I don't understand what is going on. Do I miss a C++ (template?) language feature here and am I doing something wrong, or is the g++ compiler wrong in not accepting the first piece of code?
Note BTW that this also compiles ...
template<typename A, typename B>
class Foo { };
void method ( Foo<int,int> const& stuff = Foo<int,int>() );
I am not so sure that this is a bug in g++ (since version 4.2.4). The code now passes in g++ 4.4 (see UPDATE below). In order to have this code compile for other versions of compilers you can add a set of parenthesis around the default argument:
template<typename A, typename B>
class Foo { };
struct Bar {
void method ( Foo<int,int> const& stuff = ( Foo<int,int>() ) );
};
IMO, these parenthesis are necessary as there is an additional requirement that the default argument can refer to a member of the class that may be declared later in the class body:
struct Bar {
void method ( int i = j); // 'j' not declared yet
static const int j = 0;
};
The above code is legal, and when the declaration for 'method' is being parsed the member 'j' has not yet been seen. The compiler therefore can only parse the default argument using syntax checking only, (ie. matching parenthesis and commas). When g++ is parsing your original declaration, what it is actually seeing is the following:
void method ( Foo<int,int> const& stuff = Foo<int // Arg 1 with dflt.
, int>() ); // Arg 2 - syntax error
Adding the extra set of parenthesis ensures that the default argument is handled correctly.
The following case shows an example where g++ succeeds but Comeau still generates a syntax error:
template<int J, int K>
class Foo { };
struct Bar {
void method ( Foo<0, 0> const & i = ( Foo<j, k> () ) );
static const int j = 0;
static const int k = 0;
};
EDIT:
In response to the comment: "You could just as well have a function call with multiple arguments there", the reason that this does not cause a problem is that the comma's inside the function call are surrounded in parenthesis:
int foo (int, int, int);
struct Bar {
void method ( int j =
foo (0, 0, 0) ); // Comma's here are inside ( )
};
It is possible therefore, to parse this using the syntax of the expression only. In C++, all '(' must be matched with ')' and so this is easy to parse. The reason for the problem here is that '<' does not need to be matched, since it is overloaded in C++ and so can be the less than operator or the start of a template argument list. The following example shows where '<' is used in the default argument and implies the less than operator:
template<int I, int J>
class Foo { };
struct Bar {
template <typename T> struct Y { };
void method ( ::Foo<0,0> const& stuff = Foo<10 , Y < int > = Y<int>() );
struct X {
::Foo<0, 0> operator< (int);
};
static X Foo;
};
The above "Foo<10" is a call to the "operator<" defined in 'X', not the start of the template argument list. Again, Comeau generates syntax errors on the above code and g++ (including 3.2.3) parse this correctly.
FYI, the appropriate references are a note in 8.3.6/5:
[Note: in member function declarations, names in default argument expressions are looked
up as described in 3.4.1...
and then in 3.4.1/8
A name used in the definition of a member function (9.3) of class X following the function’s declaratorid29)
shall be declared in one of the following ways:
...
— shall be a member of class X or be a member of a base class of X (10.2), or
This bullet here, is the part that forces the compiler to "delay" lookup for the meaning of the default argument until all of the class members have been declared.
<UPDATE>
As pointed out by "Employed Russian", g++ 4.4 is now able to parse all of these examples. However, until the DR has been addressed by the C++ standards committee I am not yet ready to call this a "bug". I believe that long term extra parenthesis will be required to ensure portability to other compilers/tools (and maybe even future versions of g++).
It has been my experience that the C++ standard does not dictate that compiler vendors should all use the same parser technology and they also cannot expect that all technologies are equally powerful. As a result, parsing requirements normally don't require that vendors perform superhuman feats. To illustrate this consider the following two examples:
typedef T::TYPE TYPE;
T::TYPE t;
If 'T' is dependent, then given each context 'TYPE' must be a typename, however the standard still requires the typename keyword. These examples are unambiguous and can only mean one thing, however the standard (in order to allow for all parser technologies) still requires the typename keyword.
It's possible that the DR may be addressed in such a way that a compiler which fails to parse these examples will still be "standard conforming" as long as extra parenthesis allows the code to parse.
</UPDATE>
This is a known bug in gcc.
It has been fixed in gcc-4.4, which compiles the original source just fine.
Looks like a compiler bug. I tried it on IBM's xlC compiler V7.0 (which I've found to be more standard-conforming than gcc) and it compiles okay.
template <bool> class A {};
typedef A<static_cast<bool>(1>0)> B;//buggy
int main() { B b; }
OK, suppose I want to check whether the template parameter has a nested type/typedef XYZ.
template <class T>
struct hasXZY
{
typedef char no;
typedef struct { char x[2]; } yes;
template <class U>
static yes f(typename U::XYZ*);
template <class /*U*/>
static no f(...);
enum {value = sizeof(f<T>(0))==sizeof(yes)};
};
Works fine, as expected.
Now consider this:
template <class T>
struct hasXZY
{
typedef char no;
typedef struct { char x[2]; } yes;
static yes f(typename T::XYZ*);
static no f(...);
enum {value = sizeof(f(0))==sizeof(yes)};
};
hasXYZ<int> now results in a compile-time error. OK, f is not a template function. But on the other hand when hasXYZis instantiated for int via hasXYZ<int>::value, the compiler could easily just exclude f(int::XYZ*) from candidate list. I just don't understand why a failure in the instantiation of a member functions declaration in a class template must make the whole class instantiation fail. Any ideas?
Edit: My question is: why should the member function declararions be all well-formed? Since the compiler instantiates the methods only upon their usage, why does it need correct declaration. Consider the above example2 as a possible use-case of this feature.
SFINAE is used only when creating a candidate set for a function overload resolution. In your first example, you are calling the overloaded f() function, and the first one is excluded thanks to SFINAE.
In your second example, when instantiate hasXZY, all its members must be well defined, and the substitution of the template parameter must not fail. It does for int::XYZ.
Members will not be excluded from the class because of a substitution failure.
I'm not a C++ language lawyer, but I'll have a go at this.
In your second example, the member functions must be well-defined because they are no longer template functions once hasXZY is instantiated for int. To convince yourself of this, do the substitution for T "by hand":
struct hasXYZ
{
typedef int T;
typedef char no;
typedef struct { char x[2]; } yes;
static yes f(T::XYZ*);
static no f(...);
enum {value = sizeof(f(0))==sizeof(yes)};
};
int main()
{
std::cout << hasXYZ::value << "\n";
}
and observe that this fails to compile, with the same compiler error as before (in GCC, at least):
foo.cc:9: error: ‘T’ is not a class or namespace
By contrast, the first example compiles and behaves as expected after manual instantiation; the f members are still templated on U.
Edit: My question is: why should the member function declararions be all well-formed? Since the compiler instantiates the methods only upon their usage, why does it need correct declaration. Consider the above example2 as a possible use-case of this feature.
When implicitly instantiating a class template specialization, the compiler has to inspect the complete declarator of that member because it needs to know basic information about the declaration. Such can contribute to the size of the class template specialization.
If inspecting the declaration part will find out it's declaring a data-member, the sizeof value of the class will possibly yield a different value. If you would have declared a function pointer instead, this would be the case
yes (*f)(typename T::XYZ*);
The C++ language is defined in such a way that the type of a declaration is known only once the whole declaration is parsed.
You can argue that you put static there, and thus in this case this is not needed to compute its size. But it is needed for name-lookup to know what a name hasXZY<T>::f refers to and that there was declared a name f at all. The compiler will not instantiate the definition of hasXYZ::f, but it will only instantiate the non-definition part of the declaration, to gets its type and adding its name to the class type for name lookup purposes. I believe supporting delayed-instantiation for declaration of names in particular cases where it would possibly work would complicate implementation of C++ compilers and the C++ spec even more, for no comparable benefit.
And finally, in your example where you attempt to call it, the compiler has to instantiate the declaration, because it needs to lookup the name f, and for this it needs to know whether that declaration is a function or something else. So I really even theoretically can't see a way your example could work without instantiating the declaration. Note that in any case, these will not instantiate a definition of the function.