I have the following code where I'm expecting decltype() to not work on Derived class to get run() base class method return-type, since the base class does not have a default constructor.
class Base
{
public:
int run() { return 1; }
protected:
Base(int){}
};
struct Derived : Base
{
template <typename ...Args>
Derived(Args... args) : Base{args...}
{}
};
int main()
{
decltype(Derived{}.run()) v {10}; // it works. Not expected since
// Derived should not have default constructor
std::cout << "value: " << v << std::endl;
//decltype(Base{}.run()) v1 {10}; // does not work. Expected since
// Base does not have default constructor
//std::cout << "value: " << v1 << std::endl;
}
I'm aware you can use declval<> to get member functions without going through constructors, but my question is why decltype works here. I tried to find in the C++ standard something relevant, but did not find anything. Also tried multiple compilers (gcc 5.2, 7.1 and clang 3.8) and have the same behavior.
Behold the magic of unevaluated contexts... and lying.
Actually trying to do something like:
Derived d;
will be a compile error. It's a compiler error because in the process of evaluating Derived::Derived() we have to invoke Base::Base(), which doesn't exist.
But that's a detail of the implementation of the constructor. In an evaluated context, we certainly need to know that. But in an unevaluated context, we don't need to go so far. If you examine std::is_constructible<Derived>::value, you'd see that is true! This is because you can instantiate that constructor with no arguments - because the implementation of that constructor is outside of the immediate context of that instantiation. This lie - that you can default-construct Derived - allows you to use Derived{} in this context, and the compiler will happily allow you to go on your merry way and see that decltype(Derived{}.run()) is int (which also does not involve actually invoking run, so the body of that function is likewise irrelevant).
If you were honest in the Derived constructor:
template <typename ...Args,
std::enable_if_t<std::is_constructible<Base, Args&...>::value, int> = 0>
Derived(Args&... args) : Base(args...) { }
Then decltype(Derived{}.run()) would fail to compile, because now Derived{} is ill-formed even in an unevaluated context.
It's good to avoid lying to the compiler.
When an expression inside decltype involves a function template, the compiler only looks at the template function's signature to determine whether or not the template could be instantiated if the expression were really in an evaluated context. The actual definition of the function is not used at that point.
(In fact, this is why std::declval can be used inside decltype even though std::declval has no definition at all.)
Your template constructor has the same signature as though simply declared but not yet defined:
template <typename ...Args>
Derived(Args&... args);
While processing the decltype, the compiler just looks at that much information and decides Derived{} is a valid expression, an rvalue of type Derived<>. The : Base{args...} part is part of the template definition and doesn't get used inside decltype.
If you want a compiler error there, you could use something like this to make your constructor more "SFINAE-friendly", which means information about whether or not a specialization of the template is actually valid gets put into the template signature.
template <typename ... Args, typename Enable =
std::enable_if_t<std::is_constructible<Base, Args&...>::value>>
Derived( Args& ... args ) : Base{ args... }
{}
You might also want to modify the constructor to avoid "too perfect forwarding". If you do Derived x; Derived y{x};, the template specialization Derived(Derived&); will be a better match than the implicit Derived(const Derived&);, and you'll end up trying to pass x to Base{x} rather than using the implicit copy constructor of Derived.
Related
How do lines (2) and (3) even compile in the following C++ class, given that this is a pointer, so should need -> notation to access fields (as seen in line (1))? (Source)
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
template <typename T>
class sptr_wrapper
{
private:
boost::shared_ptr<T> sptr;
public:
template <typename ...ARGS>
explicit sptr_wrapper(ARGS... a)
{
this->sptr = boost::make_shared<T>(a...);
}
explicit sptr_wrapper(boost::shared_ptr<T> sptr)
{
this->sptr = sptr; // (1)
}
virtual ~sptr_wrapper() noexcept = default;
void set_from_sptr(boost::shared_ptr<T> sptr)
{
this.sptr = sptr; // (2)
}
boost::shared_ptr<T> get_sptr() const
{
return sptr; // (3)
}
};
The line (2) is invalid. As you said, this is a pointer, we need to use -> instead of .
As the member of class template, sptr_wrapper::set_from_sptr is not required to be instantiated, until it's used. So you can add some code trying to call it, then you might get compile-errors as you expect.
This applies to the members of the class template: unless the member is used in the program, it is not instantiated, and does not require a definition.
The line (3) is valid; sptr refers to the member sptr, which has the same effect as this->sptr.
When a non-static class member is used in any of the contexts where the this keyword is allowed (non-static member function bodies, member initializer lists, default member initializers), the implicit this-> is automatically added before the name, resulting in a member access expression (which, if the member is a virtual member function, results in a virtual function call).
Would you believe that the reason this compiles is because nothing really gets compiled here?
The shown code defines a template.
A template does not become "real" until it instantiates a class. Only at that time the compiler gets a closer look at the template, and attempts to figure WTF it's doing.
Sure, when defining a template the compiler makes a half-hearted attempt to parse the template, but only barely enough to satisfy itself that the template consists of some plausibly-looking C++ code.
If you add some additional lines to the shown code you'll get the compilation errors you were yearning for:
class X {};
void foo()
{
sptr_wrapper<X> x;
boost::shared_ptr<X> y;
x.set_from_sptr(y);
}
And this produces the compilation errors you were looking for:
t.C:27:14: error: request for member ‘sptr’ in ‘(sptr_wrapper<X>*)this’, which is of pointer type ‘sptr_wrapper<X>*’ (maybe you meant to use ‘->’ ?)
27 | this.sptr = sptr; // (2)
Note that merely instantiating
sptr_wrapper<X> x;
isn't enough. You have to go full throttle and invoke the method in question, before it becomes "real" in the eyes of a C++ compiler, and it chokes on it.
It's true that I can quite think of any circumstance where "this.foo" might be valid C++ code, but I'm sure that somewhere in the 2000 pages that make up the current C++ standard, the exact details of what's going on gets spelled out in a very pedantic way.
And you might consider dropping a note to your compiler's bug tracker, a feature request to have your compiler issue a friendly warning, in advance, when it sees something like this.
This version doesn't compile at all:
struct A {
void foo() {
static_assert(0,"Fail");
}
};
This version compiles without errors (at least in my version of compiler):
template <int x>
struct B {
void foo() {
static_assert(x,"Fail");
}
};
B<0> b;
The second version fails to compile only when I call b.foo();, so I want to know is it permitted by the standard to use the second version if I never call method foo? Will all compilers behave in the same way? Isn't it undefined behavior?
I want to include static_assert in the code to forbid usage of some methods of a template class when some template parameters meet some criteria. Is it correct usage of static_assert?
I want to use this approach (I want to forbid usage of .z() when vector has only two dimensions) in this situation:
template <typename T, int D>
struct MyVecctor {
MyVecctor() : data({})
{}
template <typename... Args>
MyVecctor(Args... args) : data({args...})
{
static_assert(D > 0);
static_assert(sizeof...(args) == D);
}
T& operator[] (std::size_t index) {
return data[index];
}
T& x() {
static_assert(D>=1);
return data[0];
}
T& y() {
static_assert(D>=2);
return data[1];
}
T& z() {
static_assert(D>=3);
return data[2];
}
std::array<T, D> data;
};
The behavior here is well-defined. The significant difference here is that in second case result of static_assert depends on template parameter so it won't be resolved until this method is instantiated. If it didn't depend on template parameter then it would fail just like in first case without instantiating anything:
template <int x>
struct B {
void foo() {
static_assert(0,"Fail");
}
};
And yes, forbidding usage of some methods of a template class when some template parameters met some criteria is a correct usage of static_assert. And I would even say this is a preferred method of prohibiting method because it may yield a more readable error message (even with a potential fix suggestion) compared to usual template instantiation failure gibberish.
The bodies of a template class's methods are not instantiated unless called.
However, if we consider the instantiation of bodies of a template class's methods to be template instantiations (this is unclear in the standard), then there must be a valid set of template arguments that makes the body possible to instantiate; otherwise the program is ill-formed, no diagnostic required.
In your specific case, static_assert(x, "Fail") clearly has a valid instantiation (any x!=0). So you are safe.
However
void foo() {
static_assert(x&&!x, "Fail");
}
isn't going to be safe; by my reading, that is an ill-formed program with no diagnostic required. On the other hand, my reading might be wrong; the standard is pretty oblique here.
The philosophical reason why the above is wrong is that it permits compilers to detect for impossible assumptions in static asserts; it lets the compiler check more things in the bodies of templates than the standard demands, which is I believe why the standard makes uninstantable templates ill-formed, and no diagnostic requried is because they don't want to have to force every compiler to do every kind of diagnostic for every kind of uninstantiable template (which requires solving Halt).
So philosphically, your static_asserts in non-template methods should be possible to pass for some template arguments passed to the containing template class.
Things get murkier when you have a template method to a template class which is impossible to instantiate for certain template arguments to the template class. But that is going down a rabbit hole.
I am sufficiently uncertain about that last case that I avoid doing it, and instead use CRTP to conditionally have the method exist or not.
With C++2a, you might use requires:
void foo() requires (x != 0)
{
/*..*/
}
Demo
Static Assertion static_assert(bool_constexpr, message) performs compile-time assertion checking.
If bool_constexpr returns true, this declaration has no effect.
Otherwise a compile-time error is issued, and the text of message, if
any, is included in the diagnostic message.
Your code is static_assert(0,"Fail") Because of that 0 it will assert. But, if the bool expresion depends on a template parameter (or a function parameter), it has no value at compile time (unless you use a default value), and can not assert.
static_assert(x,"Fail") may assert if the compiler knows that x= false. Using B<0> b is not enough for the compiler. The assertion is done inside foo(). If this member is used, which means that this member function is instantiated, then the compiler does assert.
Can I put static_assert in class method if I never call this method?
A static assert declaration may appear at namespace and block scope
(as a block declaration) and inside a class body (as a member
declaration)
So, yes, you can.
I have a templated class where I ensure the type parameter is a subclass of some abstract base class like this:
#include <type_traits>
template<typename T>
class Foo
{
static_assert(
std::is_base_of<MyBase, T>::value,
"T must be a descendant of MyBase"
);
void SomeMethod()
{
auto bar = T();
//or
auto bar = T("Constructor with parameter")
bar.someFunctionOnMyBase();
}
};
Now in programming languages like C# or Java I can use this information and use he type information to call methods on the template type. Is such thing possible? Bonus points if it also possible to call constructors with the correct parameters.
Yes, this is totally fine (as long as both the constructor that you intend to call and the destructor of class T are publicly accessible).
As a matter of fact you don't even need to have the static_assert, and as long as a member function T::someFunctionOfMyBase exists (even if it isn't the one defined in MyBase and just happens to share the name), this will still compile.
Condider the following code:
template <class Impl, class Cont>
struct CrtpBase
{
void foo()
{
cout << "Default foo\n";
Cont cont;
cont.push_back(10); // Going to fail if Cont::push_back doesn't exist
}
};
typedef std::unordered_map<int,int> ContType;
struct Child : public CrtpBase<Child, ContType>
{
typedef CrtpBase<Child, ContType> _Parent;
// using _Parent::foo // (1)
void foo() { cout << "Child\n"; }
};
int main()
{
Child obj;
obj.foo(); // (2)
return 0;
}
What I'm stuck at is conditions when CrtpBase class is instantiated and when it isn't.
At point (2), when I call foo(), to my point of view, the compiler should generate a list of possible overloads. These are to be Child::foo() and Child::_Parent::foo(). Therefore Child::_Parent::foo() has to be instantiated. (At this point compilation should fail since the error is in the body function, SFINAE not applicable) Then the compiler should choose the priority match.
However the program compiles and shows that CrtpBase::foo is not instantiated. The question is why. The compiler somehow knows that Child::foo would be the best match and stops overload resolution process.
When I uncomment // using ... , asking compiler explicitly to use base function overload to be one of the matches, nothing changes, it compiles again
Just to make a sort of conclusion of what I have understood from the answers below as they cover different aspects of what's going on
The compiler doesn't even consider the base overload. That is the crucial fact that I didn't see, hence all the misunderstanding
That is not the case here, but choosing the best match during the overload resolution instantiates only the chosen match
At point (2), when I call foo(), to my point of view, the compiler should generate a list of possible overloads.
Trueish. We start by looking up the name foo in Child. We find Child::foo and then stop. We only have one candidate, which is a viable candidate, so we call it. We don't continue to look in the base classes, so CrtpBase::foo is never considered. This is true regardless of signatures (if CrtpBase::foo() took an int, obj.foo(4) would fail to compile because Child::foo() doesn't take an argument - even if a hypothetical call to CrtpBase::foo(4) would be well formed).
When I uncomment // using ..., asking compiler explicitly to use base function overload to be one of the matches, nothing changes, it compiles again
That's actually not what you're doing. The using-declaration brings the name CrtpBase::foo into the scope of Child, as if it were declared there. But then the declaration of void foo() hides that overload (otherwise the call would be ambiguous). So again, name lookup for foo in Child finds Child::foo and stops. Where this case differs from the previous case is if CrtpBase::foo() took different arguments, then it would be considered (such as my hyptohetical obj.foo(4) call in the previous paragraph).
Template classes instances can be instantiated without their methods being instantiated.
Child is not a template class instance. Its methods are always instantiated.
Simply doing overload resolution on a template class instance method name does not trigger template class method instantiation. If it not chosen, it is not instantiated.
This feature originally added in the prehistory of C++. It permitted std::vector<T> to have an operator< that would conditionally work depending on if T had an operator< in the pre-SFINAE era of C++.
For a simpler case:
template<class T>
struct hello {
void foo() { T t{}; t -= 2; }
void foo(std::string c) { T t{}; t += c; }
};
Live example.
Calling hello<int>.foo() considers hello::foo(std::string) and does not instantiate it.
Calling hello<std::string>.foo("world"); considers hello::foo() and does not instanitate it.
Background
Consider the following code:
struct A { // a class we want to hide from the client code
int f();
int f(char);
int g();
};
struct B : A {}; // the interface class
// client code:
//
// It's natural to assume a member function of T should have
// a type of int (T::*)(), right?
template <typename T, int (T::*)()> struct F {};
// compiles, of course
F<A, &A::g> {};
// doesn't compile, because &B::g is not of type `int (B::*)()`, and no
// conversion is allowed here
F<B, &B::g> {};
// compiles, because &B::g is actually &A::g
//
// but this is not I want because the class hierarchy may be deep and it's
// not easy to know what A is.
F<A, &B::g> {};
The fragment struct<B, &B::g> {} doesn't compile because
The type of &B::g is int (A::*)(), rather than int (B::*)();
Though an implicit conversion from int (A::*)() to int (B::*)() is legal and viable, the C++ standard forbids (!!) any conversion when doing the template arguments substitution for the point-to-member-function template parameter. (Strictly speaking, a conversion to nullptr_t is allowed.)
As a consequence, F<B, &B::g> cannot match the exact definition of F and it fails to compile. This is sad because class A may be the implementation detail we don't want to bother with.
Workaround
A naive hack would be
template <typename T, T val> struct G {};
// at least compiles, and don't have to be aware of A
G<decltype(&B::g), &B::g> {};
so far so good.
Problem
The above hack doesn't work with the overloaded class methods. Usually, the overloads can be resolved by static_cast, but this requires we know the exact type of &B::f -- a nice chick-and-egg situation.
// won't compile since &B::f is ambiguous
G<decltype(&B::f), &B::f> {};
// won't compile just like F<B, &B::g>
G<decltype(static_cast<int(B::*)()>(&B::f)), &B::f> {};
// compiles, but require the knowledge of the type of &B::f
G<decltype(static_cast<int(A::*)()>(&B::f)), &B::f> {};
Something like G<decltype(static_cast<int(A::*)()>(&B::f)), &B::f> {}; is terrible.
To wrap up, the problem is how to correctly select a particular overload and avoid mentioning the base class A when &B::f is actually &A::f.
Any thoughts?
I found a workaround fitting my requirements. Hoping it be helpful for anyone in need. I got stuck for days...
The idea is using a function template to match a particular overload, and then pass the correct type to G. A layer of indirection always saves the world.
template <typename T>
auto forward_pmf_with_signature( int(T::*pmf)() ) -> decltype(pmf);
G<decltype(forward_pmf_with_signature(&B::f)), &B::f> {}; // works
G is used without the awareness of A and selects the correct overload, cool.
Now the new problem is that I found G<decltype(forward_pmf_with_signature(&B::f)), &B::f> is redundant and error-prone. It's trivial to use a macro USE_G(&B::f) to simply the code, but it seems to me that it's not easy, or even possible to do the simplification in a semantic way.