Instantiation of a hidden function - c++

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.

Related

In the Curiously Recurring Template Pattern (CRTP), when does the dependent type name become a complete type?

I want to have classes with a static data member knowing the class's complete size. This is for storing singleton instances, in case you want to know the actual use case of this.
In my naive implementation of this feature, I wanted to use a mixin class to add the special data member to my class. The mixin class would have to know the complete class (in order to know the complete class's size), so I implement it using the Curiously Recurring Template Pattern, a little bit like this:
template<class ObjectType>
class SingletonOf
{
static inline /* some type same size as ObjectType */ instance_memory;
public:
void *operator new(std::size_t)
{
return &instance_memory;
}
void operator delete(void *)
{
}
};
class foo : public SingletonOf<foo> // CRTP used here, to let SingletonOf know foo
{
// foo data members...
// foo member functions...
};
void bar() {
foo *p = new foo; // calls SingletonOf<foo>::operator new and returns the instance memory
}
Cute, right? Well, I learned that the following in C++20 is ill-formed (note: in all the code samples below, the class foo and the function bar() do not change. Also I will not keep writing the empty definition of SingletonOf::operator delete, because you can remember that it's there):
template<class ObjectType>
class SingletonOf
{
static char inline instance_memory[sizeof(ObjectType)]; // syntax error: incomplete type
public:
void *operator new(std::size_t) { return instance_memory; }
...
Now, we will all agree the reason why that is ill-formed - and I am not complaining, just informing - is that ObjectType is foo, and until the closing brace of foo, foo is an incomplete type. And, obviously, sizeof cannot be called on incomplete types. So, I am fine with that. However, the following using a nested class-template does work - at least according to clang++ in c++20 mode, I think?
template<class ObjectType>
class SingletonOf
{
template<class CompleteObjectType>
struct InstanceMemory
{
static char inline instance_memory[sizeof(CompleteObjectType)];
};
public:
void *operator new(std::size_t) {
return InstanceMemory<ObjectType>::instance_memory;
}
...
Now my question is: why does that work? Or, let's start with the more fundamental question: does that work, actually? As of this writing, just to be clear, I have not verified that bar() actually calls the intended operator new and returns the foo-sized instance memory. Probably, should do that. But I'm busy. What I do know at this time, is that my clang++ in c++20 mode compiles it. This compilation includes compiling the function bar(), which allows me to be certain it instantiates the template. So that is to back up my contention that the compiler is accepting it. There are no errors or warnings give, just an output object file.
If I am right that this second code is well-formed, then it looks like ObjectType (= foo) in the body of operator new in the second code sample, is considered a complete type. How did that happen?
This isn’t really any different from having InstanceMemory defined in a namespace: until it is instantiated, its template argument need not be complete. This separation works because it removes the presumption that you should be able to use decltype(SingletonOf::instance_memory) immediately after declaring it.
When SingletonOf<ObjectType> is being instantiated, ObjectType is incomplete. That's why you can't get the size of it.
However, the member function bodies of SingletonOf work as if they are placed just after the type. And those functions get instantiated at a point when ObjectType is complete. This is why ObjectType is complete and visible to member functions of SingletonOf<ObjectType>.
Your inner struct InstanceMemory is itself a template. And you instantiate it within a member function of the outer template. Since that member function sees ObjectType as complete, so too does InstanceMemory<ObjectType>.
All you have to do is make sure to instantiate InstanceMemory<ObjectType> at a point where ObjectType is complete.

Can I put static_assert in class method if I never call this method?

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.

decltype() variadic template base class

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.

Executing a specific function depend on type

I want to make a function which can handle different kind of things depend on type all in one.
I know Overloading is a nice solution for it. Just like,
class C1 {...};
class C2 {...};
void handle(C1& c1){...}
void handle(C2& c2){...}
But there are so many duplicated code in Overloading way since these 2 initialization is the same. That's why I want to wrap them together.
I have some ideas for my purpose.
For example,
class C1 {...};
class C2 {...};
void handle_C1(C1 &c1);
void handle_C2(C2 &c2);
template<typename T>
void handle(T &content){
// Initialization was done here
if (std::is_same(C1, T))
handle_C1(content);
if (std::is_same(C2, T))
handle_C2(content);
}
A compilation error was found error that handle_C2 mismatch parameters because handle_C2 parameter type is C2 when I call
C1 c1;
handle_C1<C1>(c1);
According to SFINAE in C++, I expect the compile would ignore this substitution failed, but it doesn't.
Is there anyone can give me a advice?
Is the best solution overloading? if it's true, how can I reduce my duplicated code.
It seems to me you are overthinking the problem. Simply define overloads without the initialization code and define a handle function that takes advantage of overload resolution.
class C1 {...};
class C2 {...};
void handle_impl(C1& c1){...} // Remove initialization from your overloads
void handle_impl(C2& c2){...}
template<typename T>
void handle(T &content)
{
// Initialization is done here
// Let the compiler resolve overloads for you
handle_impl(content);
}
When handle() is instantiated with a T, the full body of the function is instantiated. Both of the "if"s and their bodies are compiled. While you and I know that is_same() is a compile time constant and you may expect the compiler to ignore the impossible case, the way it's used is as a runtime value and the compiler must process both ifs and semantically checked "as if" they were both potentially called (even if the optimizer can eliminate one, that's after the validity testing the compiler does.) You thus end up with code calling handle_C1 and handle_C2, both passed the same type, and one is sure to be invalid and fail to compile.
If you can use c++17, a new feature directly addresses this problem, called "constexpr if" which makes the body of the if get processed (aside from valid statements and syntax) if the constexpr is true:
template<typename T>
void handle(T &content){
// Initialization was done here
if constexpr (std::is_same(C1, T))
handle_C1(content);
else if constexpr (std::is_same(C2, T))
handle_C2(content);
}
If you don't have a c++17 compiler, (or even if you do!) you should consider going back to your original design and simply factor out the initialization from each of the functions and make it a generic helper function.
I think in your case overloading is all you need. Simply write another method for you special initialization that handles each class and do your general stuff in the templated version (online):
void handleSpecial(C1& c1) {
std::cout << "Handling C1\n";
}
void handleSpecial(C2& c2) {
std::cout << "Handling C2\n";
}
template <typename T>
void handle(T& content) {
std::cout << "Doing generell stuff\n";
handleSpecial(content);
}
SFINAE only works before choosing which function to call, which in this case can't happen because the function can be called with any type. Then the whole body will be generated and you get the compiler error, because there's no function handle_C1 for C2 and vice versa.
Going through your steps:
If you have multiple classes that are handled the same for many things, but have individual character, then this could call for a base class. E.g. You could have something like:
class CBase { ... common features of C1, C2, C3, ...
public:
~CBase() {};
virtual void handle() = 0;
};
class C1 : public CBase { ... something specific to C1
public:
virtual void handle() { do what has to be done for C1 }
};
class C2 : public CBase { ... something specific to C2
public:
virtual void handle() { do what has to be done for C2 }
};
Now, it sounds a bit like this would already solve the issue, since you can call C1.handle() or CBase->handle(). If you need it to be done through an external function, you could do:
void handle(CBase *base_ptr) {
base_ptr->handle();
}
Personally, I find this nicer than passing by non-const reference anyways, but I know that this is a big debate.
About your SFINAE:
SFINAE just says that if the template substitution fails, the compiler will keep looking for a match and not throw an error directly. But it will throw an error if it does not find a match at all. In your case, if e.g. T = C2, it will still try to compile handle_C1(C2), which fails. (Your "if case" is a run-time decision, whereas the compiler makes these decisions at compile time)
You could revert to the good old pointer aliasing. It is allowed to cast a pointer to a type to a pointer to another type, it is simply UB to derefence a pointer if the value pointed has not the correct type.
But here you know that at run time the type will be correct, because the std::is_same guarantees it. So I would write:
template<typename T>
void handle(T &content){
// Initialization was done here
if (std::is_same<C1, T>()) {
C1* c = reinterpret_cast<C1*>(&content);
handle_C1(*c);
}
if (std::is_same<C2, T>()) {
C2* c = reinterpret_cast<C2*>(&content);
handle_C2(*c);
}
}
It just costs an automatic pointer allocation and copy that a really clever optimizing compiler could avoid because at low level machine code it is clearly a no-op.
Here's another solution that simply extracts the code common to both behaviors into its own function:
class C1 { /* ... */ };
class C2 { /* ... */ };
template<typename T>
void handle_init(T& content) {
// Common initialization code
}
void handle(C1& c1) {
handle_init(c1);
// C1-specific code
}
void handle(C2& c2) {
handle_init(c2);
// C2-specific code
}
Depending on the code inside handle_init, the template might not be necessary if, for example, the body of the function only calls "getter" methods of content, which can instead be called beforehand and passed to handle_init by the handle functions.
I named both functions handle to be consistent with the code you provided, but you can give them different names and it will still work for most use cases. This is the case for most uses of overloading, since overload resolution uses the compile-time type of the argument, which you as the programmer usually know anyways at the invocation site.
A big exception to this is if the argument to handle is a template parameter, in which case you'd need to use overloading (so some of the other answers actually do require overloading because of this). Note that to call different functions based on the run-time type of an argument, overloading doesn't work and you'd need virtual methods like in Cedric's answer.
But aside from the above paragraph, there isn't really a difference in functionality between having one function that handles arguments of different types, and having different functions for each argument type. Of course, maybe in your problem domain it makes sense to think of it as the former. And calling both functions handle using overloading would provide a notational convenience. I just think it's good to be consciously aware of this when doing it, in case it turns out that overloading doesn't have the functionality you need.

How compiler overloades template function without argument list change?

As you can see in the code below method get is templated.
struct A
{
int i;
char c;
};
class SL
{
static void* m_a;
public:
template <class T>
static T* get()
{
return static_cast<T*>(m_a);
}
};
void* SL::m_a = new A{12, 'C'};
int main()
{
std::cout << SL::get<A>()->i;
return 0;
}
What I do not understand is when I write SL::get<B>() how a compiler creates two methods with the same name, in the same namespace, with two different return types that both does not have parameters, i.e. does not have different argument list? How they overload each-other? Or it is wrong to understand temple function generation to have the same names and interpret their call via overload resolution?
Template instantions are really different functions which are not overloaded.
You may think of the template parameters <B> as a part of the function name.
So SL::get<A> and SL::get<B> are really different functions (though stemming from the same template).
Quoting from cppreference:
Template argument deduction takes place after the function template name lookup (which may involve argument-dependent lookup) and before overload resolution.
As you see, overload resolution is a different process.
This is compiler specific and you shouldn't worry about it too much. A general idea of how it's done though:
Say you call the templated method with 2 different types
SL::get<A>();
SL::get<B>();
The compiler generates 2 new methods for these calls:
static A* get_a()
{
// etc..
}
and
static B* get_b()
{
// etc..
}
This may differ from compiler to compiler but it shows how a compiler avoids name clashes. To the programmer, it's the same method being called twice, to a compiler it's just 2 different methods called by different pieces code.
Templates are prone to name mangling as anything else. When the exact get is generated, it's not really called get anymore, it will be called something like get#YUQIE or similar. You can check this article for an example. This is implementation-defined though, so different compilers will do it in a different way. For example, in the following code
template <class T>
T get()
{
return T();
}
int main()
{
get<int>();
get<char>();
return 0;
}
get was mangled by gcc as _Z3getIiEPT_v for get<int> and _Z3getIcEPT_v for get<char>.