static_assert inside/outside class definition - c++

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.

Related

Why would one wrap a struct in an anonymous union? (STL msvc implementation)

The STL <memory> header (MSVC implementation) contains a class called:
template <class _Ty> class _Ref_count_obj2 : public _Ref_count_base
This class has a member:
union {
_Wrap<_Ty> _Storage;
};
where _Wrap is defined as:
template <class _Ty>
struct _Wrap {
_Ty _Value; // workaround for "T^ is not allowed in a union"
};
From my understanding, this code is designed to hold an object of type _Ty following its construction via the new operator. However I can't figure out why this was done; it seems like using a struct instead of a struct inside a union would work just as well.
Can anyone explain the reasoning behind this? Also, can anyone explain the comment in the _Wrap definition?
First, embedding the _Storage member in a union will prevent default destruction of that object (which is, more than likely, a non-trivial type); this appears to be essential, as the class involved is a reference counter. (By default, unions have a deleted destructor; see, for example: Is a Union Member's Destructor Called .)
Second, using an anonymous union 'moves' the _Storage identifier into the enclosing scope, thus removing any need for X.-style notation (if the union were to be named X). From cppreference:
Members of an anonymous union are injected in the enclosing scope (and
must not conflict with other names declared there).
Last, the need to wrap the union's member into a templated structure is so that the class will work with reference types, which are not allowed in unions. To check this last part out, try the following code with the commented-out lines made active:
template <class _Ty>
struct _Wrap {
_Ty _Value; // workaround for "T^ is not allowed in a union"
};
template<class T>
class bob {
public:
bob(T t) : uncle{t}//, aunty(t)
{
}
private:
union {
_Wrap<T> uncle;
};
// union {
// T aunty;
// };
};
int main()
{
int i = 42;
bob<int&> b{ i }; // Note: Template uses a REFERENCE type
return 0;
}

Why can't "is_base_of" be used inside a class declaration (incomplete type)?

I completely see why this cannot work:
class Base {};
class A;
static_assert(std::is_base_of<Base, A>::value, "");
Because there is no information about a 'class hierarchy', but...
Why cannot the following work?
class Base {};
class A : public Base {
static_assert(std::is_base_of<Base, A>::value, "");
};
(produce: an undefined class is not allowed as an argument to compiler intrinsic type trait)
The type 'A' is still not complete at line with static_assert (according to definition of this concept). However - the compiler already knows the 'class hierarchy' and could provide the answer for this.
Of course - this static_assert could be moved to destructor or whatever to fix this issue, but there are situations where it cannot be done, for example:
class Base {};
template<typename T>
struct type_of {
static_assert(std::is_base_of<Base, T>::value, "T is not derived from Base");
using type = int; //* Some normal type in real use
};
class A : public Base {
public:
type_of<A>::type foo(); // Will not compile
};
Should it not be allowed?
A class definition is complete (that is, a class is considered defined) after the closing brace }.
In your case, when you try to use A with std::is_base_of, A isn't fully defined yet:
class A : public Base {
// no closing brace for A yet, thus A isn't fully defined here
static_assert(std::is_base_of<Base, A>::value, "");
};
On the other side, std::is_base_of requires types that are completely defined to work.
Thus the error.
As a workaround, you can put the assert in the destructor of A:
class A : public Base {
~A() {
static_assert(std::is_base_of<Base, A>::value, "");
}
};
In fact, a class type is considered fully defined in its member functions' bodies.
See here for more details (emphasis mine):
A class is considered a completely-defined object type ([basic.types]) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, noexcept-specifiers, and default member initializers (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.
The doc page for std::is_base_of yields:
If both Base and Derived are non-union class types, and they are not
the same type (ignoring cv-qualification), Derived shall be a complete
type; otherwise the behavior is undefined.
The definition for a class is complete after you close its scope with }. Thus, in your case, an error is generated.
Note, that static assertion can appear in block or namespace / file scope. So you can move it outside the class' body or, if you don't want to have it in your header, move to the implementation file.

Variadic template constructor and copy constructor

I don't understand why the compiler chooses the copy constructor of my Production class and has no other candidate functions.
I made a minimal example to demonstrate the error:
#include <string>
#include <typeindex>
#include <iostream>
struct DummyProduction {
};
struct Dep {
};
struct Pro {
};
class ModuleBase {
};
template<typename Production = DummyProduction>
class Provider {
public:
template<typename... Dependencies>
Provider(ModuleBase& module, Dependencies... args)
{
std::cout << "Provider called!" << std::endl;
}
Provider(const Provider&) = delete;
};
class TargetController : public ModuleBase,
public Provider<Pro>,
public Provider<>
{
public:
TargetController();
private:
Dep p;
};
TargetController::TargetController() :
ModuleBase(),
Provider<Pro>(*this, &p),
Provider<>(*this),
p()
{
}
int main()
{
TargetController x;
return 0;
}
I tried it with gcc and clang. Here is a link to the non working example: link.
For the Provider<Pro>(*this, p) the right constructor is called. But for the second example Provider<>(*this) the compiler tries to call the copy-constructor.
From what I understood from the Overload resolution page all functions that match the expressions are should get inside the candidate function set. But either the variadic constuctor is not inside the set for the Provider without dependencies or the compiler chooses the copy-constructor in spite of beeing deleted.
Is there a way to avoid this behaviour?
The fact that an function/method is deleted doesn't remove it from overload list.
And the copy constructor has higher priority over the template method (as it is not an exact match).
As workaround you may cast this to the expected type:
TargetController::TargetController() :
ModuleBase(),
Provider<Pro>(*this, p),
Provider<>(static_cast<ModuleBase&>(*this))
{
}
Demo
Templated constructors are never copy constructors. When you call base's constructor and pass it a reference to base (or a derived) the copy constructor is supposed to be called. The template isn't an option in that context.
Provider<>(*this)
Is such a context.
Worth noting that I believe VS still gets this wrong. In that compiler you have to cast to base& or it'll call the template.

Must this code works?

I have the next sample code that compiles with gcc (4.7.2 using -std=c++11):
template<class C>
struct template_test
{
C testing() const
{
return C();
}
};
class A;
struct test : public template_test<A> // (1)
{};
struct A
{};
int main()
{
test t;
}
At point (1) the function template_test<A>::testing() is instantiated, and use a function of A, specifically its default constructor. Therefore, test contains this instantiated function as a function member. However, at this point A is an incomplete type, and C++ prohibits you to use members of a incomplete type.
Is this a positive gcc's error or is there another explanation?
Not only is template_test::testing() not instantiated at (1), it's never instantiated in this program. Template members are only instantiated on use, and testing() isn't used. To make this more clear, change the code slightly to:
template<class C>
struct template_test
{
C testing() const
{
return C::foo();
}
};
class A;
struct test : public template_test<A> // (1)
{};
struct A
{};
int main()
{
test t;
}
which also compiles and runs fine.
That's all right. The testing() member function won't get instantiated until you will actually invoke it. To see this, try rewriting it as follows:
C testing() const
{
static_assert(C::value, "Error!");
return C();
}
You'll see that no compilation error is issued until you try to invoke the function, but the static assert will be triggered when you add t.testing() to your main() function.
In other words, your premise that "At point (1) the function template_test<A>::testing() is instantiated" is incorrect.
You are instantiating a template with an incomplete type, this is OK.
The member function testing returns an instance of an incomplete type, which is not OK (but whether or not that's OK only comes into discussion when it's instantiated). However, you never call that function, so it is never instantiated, and thus there is no error. Instantiating the structure alone (and calling its constructor/destructor) is harmless.
Therefore, GCC is correct in letting you compile that. It would fail the moment you try to call testing.

Declaring an instance of an explicit specializtion of a template within a regular class

I can't get this to compile at all. I may not be possible but I don't know why it should not be.
class A {
template <typename T>
class B {
int test() { return 0; }
};
//template <> class B<int>; <-with this, namepace error
B<int> myB_;
};
template <> class A::B<int> {
int test() {
return 1;
}
};
As it appears the compiler complains "The explicit specialization "class A::B" must be declared before it is used."
If I try to provide the froward declaration in the commented line, the compiler complains
"The explicit specialization "B" must be declared in the namespace containing the template."
We use 2 different compilers here. This error is from IBM's "xl" compiler on AIX but I get similar errors with different verbage when compiling on our Sun systems.
It seems like a catch-22.
Obviously, this is a highly contrived, simplistic example but, it represents the problem.
I want to define a template class within a class because the template class is only relevent to the containing class. There should be no access to the template from outside the class.
Am I missing something?
You are correct. This is not possible to do (as far as I know). Your member declaration causes an implicit instantiation before the explicit specialization was declared. But how would you want to declare it? You cannot do that in class scope. Others have felt that this is an ugly restriction.
You could work around this by making the class member a pointer. This would not need to implicitly instantiate the class at that point, but rather at the point where you create the object in the end. I realize that this is an ugly work around. So better find other ways to do this.
For instance partial specializations are allowed in class scope. So you could add a dummy template parameter, then you can specialize this in the class before the member declaration. Likewise, i find this ugly, but it would not disturb things that much, I feel.
You could work around the issue by using an unnamed namespace for privacy:
namespace {
template <typename T>
class B {
int test() { return 0; }
};
template <> class B<int> {
int test() {
return 1;
}
};
}
class A {
B<int> myB_;
};
This will compile, but if A needs to be visible outside this compilation unit, you'll need more elaborate machinery (e.g., interface and factory or Pimpl).
B is not a template class and you are trying to specialize it. That is the cause for the error. You can check these two errors C2913 and C3413.
Is this what you are looking for?
class A
{
template<class T>
class B
{
inline int test()
{
return 0;
}
};
A::B<int> myB_;
};