template argument deduction for destructor - c++

Should a compiler deduce the template argument when the destructor of a class is called ?
The following piece of code :
#include <iostream>
template <typename T>
class A{
};
int main(){
A<int> * a = new A<int>();
a->~A();
}
compiles fine on gcc (g++ 4.3.4) but fails on XLC++ with
line 30.5: 1540-0210 (S) "A" is not a base class of
"A<int>"
Which of the two behavior is expect from a standard compliant compiler ?

The C++03 standard (I doubt that the C++11 standard will be any different) has the following paragraph (C++03 14.3/5 [temp.arg]):
An explicit destructor call (12.4) for an object that has a type that is a class template specialization may explicitly specify the template-arguments. [Example:
template<class T> struct A {
~A();
};
void f(A<int>* p, A<int>* q) {
p->A<int>::~A(); // OK: destructor call
q->A<int>::~A<int>(); // OK: destructor call
};
--end example]
Clause 12.4/12 [class.dtor] describes the explicit destructor call in terms of a call to a regular member function and has an example showing the explicit destructor call with both qualified and unqualified versions for the destructor's type.
To me, this clearly indicates that the intent of the standard is that each of
A<int> * a = new A<int>();
a->~A();
a->~A<int>();
a->A<int>::~A();
a->A<int>::~A<int>();
should be valid.
The fact that the first two are not mentioned in the example in 14.4/12 should not affect this, as examples are not normative.

Related

Aggregate class and non-viable constructor template

As we know, in C++20 a class that has a user-declared constructor is not aggregate.
Now, consider the following code:
#include <type_traits>
template <bool EnableCtor>
struct test {
template <bool Enable = EnableCtor, class = std::enable_if_t<Enable>>
test() {}
int a;
};
int main() {
test<false> t {.a = 0};
}
Neither GCC nor CLang compile this code. So, the class is not an aggregate, despite the fact that there is no instantiated constructor here.
Is the constructor template considered to be a "declared constructor"? What does the Standard say about such a situation?
The definition of aggregate has changed a lot, but the relevant part here has been pretty constant. The C++20 wording in [dcl.init.aggr] says:
An aggregate is an array or a class ([class]) with
no user-declared or inherited constructors ([class.ctor]),
no private or protected direct non-static data members ([class.access]),
no virtual functions ([class.virtual]), and
no virtual, private, or protected base classes ([class.mi]).
Note that it just says no declared constructors, period. Not something subtle about whether or not the constructor is viable for a particular specialization of a class template. Just none at all.
So given something like this:
template <bool B>
struct X {
int i;
X(int i) requires B;
};
X<false> still isn't an aggregate, even though its only declared constructor isn't viable for that specialization. Doesn't matter. Aggregate-ness is a property of declarations only.

Which compiler is right about inline static members of template parameter type using a Singleton CRTP?

I made a very simple Singleton base class that can be used with CRTP to make any class that derives from it a singleton. I found something strange though. Different compilers disagree on whether the code is ill-formed or not. Here it is:
template <typename Derived>
class Singleton
{
public:
inline static Derived instance;
};
As I said, it is very simple, but that is enough for demonstration purposes. An example usage is shown here:
class MyClass : public Singleton<MyClass>
{
public:
std::string msg;
MyClass() = default;
MyClass(const std::string& msg_) : msg{ msg_ }
{
}
MyClass(std::string&& msg_) noexcept : msg{ std::move(msg_) }
{
}
};
This code compiles under both GCC, and Clang. However MSVC rejects it by saying that MyClass provides no appropriate default constructor (despite it obviously being declared as default). Out of curiosity I tried using a different constructor:
template <typename Derived>
class Singleton
{
public:
inline static Derived instance{"hello MSVC"};
};
class MyClass : public Singleton<MyClass>
{
public:
std::string msg;
MyClass() = default;
MyClass(const std::string& msg_) : msg{ msg_ }
{
}
MyClass(std::string&& msg_) noexcept : msg{ std::move(msg_) }
{
}
};
This is again accepted by GCC and Clang, but rejected by MSVC, which claims this time that MyClass is undefined.
The question is: Is this code ill-formed? If it is, why? And consequently, which compilers are right? Are GCC and Clang simply taking advantage of "no diagnostic required"?
My gut feeling is that MSVC is wrong, as this is usually the case, though on the other hand, it is usually more permissive than GCC and Clang, so I am curious of the answer.
GCC and Clang are correct.
temp.inst/3:
The implicit instantiation of a class template specialization causes
the implicit instantiation of the declarations, but not of the definitions, of the non-deleted class member functions, member classes, scoped member enumerations, static data members, member templates, and friends; and
the implicit instantiation of the definitions of deleted member functions, unscoped member enumerations, and member anonymous unions.
In the context of the Singleton<Derived> instantiation, it is true that Derived is incomplete, but that doesn't matter because it's ok to declare with an incomplete type.
See also: gcc bug #71534

static_assert inside/outside class definition

Why does static_assert need to be out side of the class definition?
Failing code
#include <type_traits>
class A
{
public:
A(A&&) noexcept {}
static_assert(std::is_nothrow_move_constructible<A>::value, "ERROR");
};
int main()
{
}
Working code
#include <type_traits>
class A
{
public:
A(A&&) noexcept {}
};
static_assert(std::is_nothrow_move_constructible<A>::value, "ERROR");
int main()
{
}
And when is it appropriate to use static_asserts in the definition of a class or struct?
As far as the placement of static_assert itself is concerned both versions of your code are valid. So, no, static_assert does not need to be outside of class definition. Formally static_assert is a declaration. It is allowed wherever declarations are allowed.
The problem you are having has nothing to do with static_assert itself.
The problem here is that the expression that you use as the argument of your static_assert (std::is_nothrow_move_constructible) requires the class type to be complete to work properly. But inside the definition of class A class type A is not complete yet, which makes your argument expression invalid. This is why your static_assert works as intended only outside the class definition, where A is complete. However, this is entirely about proper usage of std::is_nothrow_move_constructible, not about static_assert itself.
Note, that inside member function bodies class type is seen in its entirety, as complete type, even if the member function is defined inside the class definition. Using this feature you can rewrite your code as
class A
{
public:
A(A&&) noexcept {
static_assert(std::is_nothrow_move_constructible<A>::value, "ERROR");
}
};
and std::is_nothrow_move_constructible<A> will produce the proper result.

Does inheriting constructors work with templates in C++0x?

In C++0x, you can use the using keyword to inherit constructors, like so:
class B { B(int) {} };
class A : public B { using B::B; };
Which will implicitly declare an A(int) constructor. Does this work with templates?
class B { B(int) {} };
template<class T> class A : public T { using T::T; };
Within T::T, I expect the compiler to figure out the left hand T since using the scope operator on template arguments is normal, but figuring out that the right hand T is the constructor is a special case. In fact it appears there's an ambiguity: what if I have a method called T in B that I'm trying to add overloads to in A (that's how a compiler would interpret such a using declaration pre-C++0x)?
Yes it works, and the reason is the name lookup mechanism. The mechanism a inheriting-constructors declaration works is simple: If the using declaration's name refers to base class constructors, that's an inheriting constructors declaration. At 3.4.3.1[class.qual]p2 we find:
In a lookup in which the constructor is an acceptable lookup result and the nested-name-specifier nominates a class C
if the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (Clause 9), or
in a using-declaration (7.3.3) that is a member-declaration, if the name specified after the nested-name-specifier is the same as the identifier or the simple-template-id's template-name in the last component of the nested-name-specifier
the name is instead considered to name the constructor of class C.
This is the paragraph that makes out of class constructor definitions work, and this is also the paragraph that makes inheriting constructor declarations work. The second bullet applies in this case:
struct B {
B(int) { }
};
typedef B mytype;
struct A : B {
// "name ... is the same as the identifier ... in the last component ..."
using mytype::mytype;
};
template<typename T> using same = T;
struct C : B {
// "name ... is the same as the template-name ... in the last component ..."
same<B>::same;
};
The latter example proves also useful in cases such as the following
template<template<typename> class Base>
struct X : Base<int> {
using Base<int>::Base;
};
In summary:
The first bullet above is a semantic rule - if the name after the nested name specifier refers to the injected class name (B::B or mytype::B), then it will be translated to refer to the constructor(s).
The second bullet is a syntactic rule - the names just must match - their meaning is irrelevant otherwise - there could have been a member called Base in the template argument provided to X, such as in the following, but the using declaration would still import the constructors and do not name the member Base:
template<typename T> struct D { private: T Base; };
X<D> x; // valid, the private member is *not* touched!
Yes, it appears it does, from the standard (Feb 2011 Draft), section 12.9:
template< class T >
struct D : T {
using T::T; // declares all constructors from class T
~D() { std::clog << "Destroying wrapper" << std::endl; }
};
Class template D wraps any class and forwards all of its constructors,
while writing a message to the standard log whenever an object of
class D is destroyed. —end example
Another thing to note, while the standard allows it, according to this list, only 1 compiler, IBM XLC++, supports this feature in a release version. GCC only currently supports it with a patch.
Edit: AJG85 pointed out that the T in the template always refers to the placeholder, so the 'using T::T' always refers to the template argument.

Keyword "typename" in Templates

Following is the code and quote is from the C++ Templates by Addison Wesley:
template <typename T>
class MyClass {
typename T::SubType * ptr;
…
};
Without typename, SubType would be considered a static member. Thus, it would be a concrete variable or object. As a result, the expression T::SubType *ptr would be a multiplication of the static SubType member of class T with ptr.
Now when I compile that code without the keyword 'typename', the error I get is this: type ‘T’ is not derived from type ‘MyClass<T>’.
Is the compiler recognizing 'T'?
If not, then shouldn't it be a undefined reference error?
If yes, then why is this an error?
Alright here is the complete code:
#include <iostream>
#include <vector>
template <typename T> class MyClass
{
T::SubType * ptr;
};
int main ()
{
return 0;
}
The error I am getting is this:
~/Desktop/notes **g++ templates/programs/trial.cpp**
templates/programs/trial.cpp:6: error: type ‘T’ is not derived from type ‘MyClass<T>’
templates/programs/trial.cpp:6: error: expected ‘;’ before ‘*’ token
Here's another way to get the same error from g++:
class Foo { static const int x = 0;};
template <typename T> class MyClass
{
Foo::x * ptr;
};
and another:
class Foo { static const int x = 0;};
class MyClass
{
Foo::x * ptr;
};
But you get a different error for this:
// class Foo { static const int x = 0;};
template <typename T> class MyClass
{
Foo::x * ptr;
};
So:
Because T is a dependent type, g++ assumes that T::SubType is an object that will be defined by the time second-phase lookup occurs. That's as expected, and is the usual reason that typename is required here.
Even if T::SubType exists and is an object, the code is still bad, just like Foo::x *ptr is bad when Foo::x exists and is an object. I still don't understand what the error message is about - how would it help for Foo to be derived from MyClass? But the error message is nothing to do with templates.
"Undefined reference" is a linker error. Since this code fails to even compile, you shouldn't expect to see "undefined reference to T" anywhere.
I don't so far see how Foo even could be derived from MyClass. I tried the following to see whether it would give a clue to the meaning of the original message, but it fails because MyClass is an incomplete type, which doesn't tell me anything about what would happen if Foo were derived from MyClass:
class MyClass
{
class Foo: public MyClass { static const int x = 0;};
Foo::x * ptr;
};
Comeau gives far more sensible error messages for all these cases - nothing about derived types, just says that T::SubType isn't a type. So explaining g++'s error message is going to take either knowledge or good guesses about g++ internals, and exactly where in the process of trying to parse your class template it finally gives up.
Without typename, SubType would be considered a static member. Thus, it would be a concrete variable or object. As a result, the expression T::SubType *ptr would be a multiplication of the static SubType member of class T with ptr.
This description is incorrect when applied to the example you give. In class bodies there can be no expressions, and no constructs are parsed as a multiplication. However, in the C++03 syntax, there is a construct that looks as follows
struct Base { int a; };
struct Derived : Base {
Base::a; // access-declaration
};
This construct was deprecated in C++03 but still supported, and means the same as the following
struct Base { int a; };
struct Derived : Base {
using Base::a; // using-declaration
};
Because you didn't tell the compiler that T::SubType is a type and hence tell the compiler that it should parse it as the type of a pointer declaration, the compiler assumed that T::SubType is the name in an access-declaration. Hence it expected a semicolon directly after it, and hence it expected that T is a base class of MyClass<T> (or that MyClass<T> is a derived class of T). The error message actually has it backwards:
if (! UNIQUELY_DERIVED_FROM_P (IDENTIFIER_TYPE_VALUE (cname),
ctype))
{
cp_error ("type `%T' is not derived from type `%T'",
IDENTIFIER_TYPE_VALUE (cname), ctype);
...
}
While the macro says
/* Nonzero iff TYPE is uniquely derived from PARENT. Under MI, PARENT can
be an ambiguous base class of TYPE, and this macro will be false. */
#define UNIQUELY_DERIVED_FROM_P(PARENT, TYPE) ...
Because T::SubType is a dependent name, you need to tell the compiler that SubType is a type (not a static data member) by typing typename keyword.
Read about dependent name here:
Name binding and dependent names (C++ only)
Dependent Names (scroll down and read this section - better if you read the complete article)
EDIT:
As for your question (which you repeated in the comment):
I think what you've posted is not the complete code. So I cannot specifically comment on that. Also, sometimes compilers aren't smart enough to point out the error in template code accurately. So I suggested you to read about dependent name and when & why typename is required. Hopefully, after that you will not have any question!