Overload resolution resolves to a function not visible yet - c++

This is kind of a follow on to this question.
#include <iostream>
struct type1 {};
struct type2 {};
void foo(type1 x)
{
std::cout << "foo(type1)" << std::endl;
}
template<typename T>
void bar() {
foo(T());
}
int main()
{
bar<type1>();
bar<type2>();
return 0;
}
void foo(type2 x)
{
std::cout << "foo(type2)" << std::endl;
}
In the above code foo(type2) is not visible at the time of instantiation of bar<type2> in main. And yet the code compiles and produces the following output :
foo(type1)
foo(type2)
How does the compiler know that foo(type2) is available when instantiating bar<type2> in main?
EDIT : I am trying to understand more about how overload resolution during template instantiation works. Consider the code below :
#include <iostream>
struct type1 {};
struct type2 {};
struct type3 {
operator type2() { return type2(); }
};
void foo(type1 x)
{
std::cout << "foo(type1)" << std::endl;
}
void foo(type2 x)
{
std::cout << "foo(type2)" << std::endl;
}
int main()
{
foo(type3());
return 0;
}
void foo(type3 x)
{
std::cout << "foo(type3)" << std::endl;
}
The output is
foo(type2)
Even though a closer match foo(type3) is available, the call foo(type3()) resolves to foo(type2) because that was the only candidate that has been parsed by the compiler until that point. Now consider the following code :
#include <iostream>
struct type1 {};
struct type2 {};
struct type3 {
operator type2() { return type2(); }
};
void foo(type2 x)
{
std::cout << "foo(type2)" << std::endl;
}
template<typename T>
void bar() {
foo(T());
}
int main()
{
bar<type3>();
return 0;
}
void foo(type3 x)
{
std::cout << "foo(type3)" << std::endl;
}
The output is
foo(type3)
That is, at the point of the call bar<type3>(), even though only foo(type2) is visible, the compiler still picks foo(type3) that comes later because that is a closer match.

Any symbol left without a definition is to be replaced during the linking process, since the function foo(type2) could've been provided in another file.
The compiler is to say whether the function needed has been defined by the end of the entire process, when no further substitution can be applied.
In order to clarify the understanding, you must be aware of the steps required to compile, say, a common C program:
first, you expand all the macros on your code;
then your code is validated according to the language syntax, so that it can be converted into assembly language -- the compilation process itself; during this step, every symbol found without a definition is annotated in a table with the entries (symbol, definition), that shall be completed later, allowing your program to be constructed properly;
next, your code compiled into assembly will be converted to machine language, i.e., the objects will be created;
finally, you need to link your already executable objects, in order to solve any dependencies on symbol definitions; this last step checks your objects for undefined symbols, adding definitions from other modules or from libraries, thus, completing the program.
If any symbol was not correctly "linked" to its definition, the compiler will point out an error in your program -- the classic undefined reference to....
Considering the code you've posted, the process would be executed until it reaches the compiler. The compiler would traverse the code, notice the definition of type1, type2, foo(type1 x), and bar<T>().
struct type1 {};
struct type2 {};
When it'd reached the main, it would find the call for bar<type1>();, and would call foo(type1()), which is already known, and can be used properly.
void foo(type1 x) {
std::cout << "foo(type1)" << std::endl;
}
template<typename T>
void bar() {
foo(T());
}
int main() {
bar<type1>();
bar<type2>();
return 0;
}
Once it'd reached the next call, bar<type2>();, it would try to call foo(type2()), but no such definition would be available for usage, so it would relate this call as an unknown symbol, that must be replaced by a definition in the later processes.
After the compiler runs through the main, it reaches a new definition, that is exactly the one lacking definition on the "translation table" being created.
void foo(type2 x) {
std::cout << "foo(type2)" << std::endl;
}
So, in the next step, the compilation is able to replace the symbol with its respective definition, and the program compiles correctly.
Regards!

The answer is found via argument-dependent name lookup (ADL) (which is also mentioned in the linked question). foo(T()); has two lookups. First at template definition time, any functions defined at the point of definition are included in the overload set. This means when the compiler sees foo(T()); inside of bar, it adds only void foo(type1 x) to the overload set. However there is a second lookup that is performed, called ADL. At template instantiation time, i.e. bar<type2>(); it looks for a foo in the same namespace as the argument which is provided, which in this case is type2. Since type2 is in the global namespace, it looks for a foo that takes a type2 in the global namespace and finds it, and resolves the call. If you are looking for info from the standard, see 14.6.4.2 Candidate functions.
Try the following and watch the code fail. This is because it cannot find foo in the same namespace as a::type1.
#include <iostream>
namespace a
{
struct type1 {};
}
template<typename T>
void bar() {
foo(T());
}
int main()
{
bar<a::type1>();
return 0;
}
void foo(a::type1 x)
{
std::cout << "foo(a::type1)" << std::endl;
}

Related

Parameterize functions with classes and access class members

I'm wondering how exactly this code works in detail (e.g. how is it able to directly access the value of TYPE).
I saw this code in a larger codebase (which is non-public, so the example is paraphrased). I've never seen this specific use-case. Is passing a template parameter like this common? Does it have a specific name/is this an idiom/pattern? When would you use this and why?
#include <iostream>
namespace FileA
{
struct Foo
{
enum TYPE
{
ENTRY,
};
void callme()
{
std::cout << "Foo\n";
}
};
}
namespace FileB
{
template <typename T>
void fun(T& obj)
{
std::cout << T::ENTRY << "\n";
obj.callme();
}
}
int main()
{
FileA::Foo f;
FileB::fun(f);
}
This will print:
0
Foo
a template is a kind of macro in the general sense of the term, so if you "expand" FileB::fun(f); T is replaced by the type of f being FileA::Foo and it is like if you have :
void fun(FileA::Foo& obj)
{
std::cout << FileA::Foo::ENTRY << "\n";
obj.callme();
}
because FileA::Foo::ENTRY is 0 the std::cout writes 0, then you apply FileA::Foo::callme() whose prints Foo
Warning, a template is much more than that, it is just a very simplified explanation of how that example works and produces the outputs, do not take it literally please ^^

Point of instantiation clarification

Consider the following code, taken from https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.cbclx01/name_binding.htm:
#include <iostream>
using namespace std;
void f(double) { cout << "Function f(double)" << endl; }
template <class A> struct container{ // point of definition of container
void member1(){
// This call is not template dependent,
// because it does not make any use of a template parameter.
// The name is resolved at the point of definition, so f(int) is not visible.
f(1);
}
void member2(A arg);
};
void f(int) { cout << "Function f(int)" << endl; }
void h(double) { cout << "Function h(double)" << endl; }
template <class A> void container<A>::member2(A arg){
// This call is template dependent, so qualified name lookup only finds
// names visible at the point of instantiation.
::h(arg);
}
template struct container<int>; // point of instantiation of container<int>
void h(int) { cout << "Function h(int)" << endl; }
int main(void){
container<int> test;
test.member1();
test.member2(10);
return 0;
}
The output is
Function f(double)
Function h(double)
I understand this, but what I don't understand, when the article states
The point of instantiation of a template is located immediately before
the declaration that encloses its use. In this example, the point of
instantiation of container is the location of the explicit
instantiation
...is why when I move the definition of void h(int) above what is labelled as the point of instantiation, h(int) still does not get called. It only gets called when I move it above the definition of the function void container<A>::member2(A).
This is the case in VS2017 and g++, so clearly either the article is badly worded or I am missing something. Can someone please clarify?
OK, the link posted above (point of instantiation and name binding) ultimately does answer this question, however it is not as clear as it could be. I have found this page and this page which further aided in my understanding, hence I post them here so others can benefit.

recognize all T of all calling function1<T> at compile time then add line function2<T>()

Is it possible to recognize all calling of a certain function in everywhere
function1<T1>(); function1<T4>(); ...
then add a line that calling this following line in a certain place?
function2<T1>(); function2<T4>(); ...
For example,
class X{
template <class T> function1(){ }
template <class T> function2(){ }
}
class A{}
class B{}
class C{}
int main(){
X x;
//vvvv this code (hopefully) will be auto generated (by macro?)
x.function2<A>();
x.function2<B>(); //<--- I don't care about order of A or B
//x.function2<C>(); //<--- this line should not be generated
//^^^^ Only X's function1 should be recognized,
// not other class's functions with the same name.
x.function1<B>();
x.function1<A>();
x.function1<B>(); .... // called in various places in many .cpp
//If it is called in another .cpp, but not here, it should still be recognized
}
Edit
My old code call :-
function2<T>() inside function1<T>()
It costs CPU significantly (I profiled), because function1 have to check whether function2 was called or not every time.
Here is a draft of a near-zero overhead solution that will work only if you are not going to invoke function1() before main():
#include <iostream>
#include <typeinfo>
template <class T>
void function2()
{
std::cout << "function2<" << typeid(T).name() << ">()" << std::endl;
}
bool dummy = true;
template <class T>
struct Func1WasInstantiated
{
struct CallFunc2
{
CallFunc2() { function2<T>(); }
void operator()() const { dummy = false; }
};
static CallFunc2 callFunc2;
};
template <class T>
typename Func1WasInstantiated<T>::CallFunc2 Func1WasInstantiated<T>::callFunc2;
template <class T>
void function1()
{
Func1WasInstantiated<T>::callFunc2();
std::cout << "function1<" << typeid(T).name() << ">()" << std::endl;
}
int main()
{
std::cout << "------- Entered main() ---------" << std::endl;
function1<int>();
function1<double>();
function1<int>();
return 0;
}
Output (check it on IDEONE):
function2<i>()
function2<d>()
------- Entered main() ---------
function1<i>()
function1<d>()
function1<i>()
The global variable dummy is the acceptor of the side effect, that ensures that Func1WasInstantiated<T>::callFunc2 is linked into the program and makes the code work as intended. Without the side effect contained in callFunc2() I can imagine an aggressively optimizing compiler eliminating that line from function1() (that is normal) and letting the linker leave out the callFunc2 objects as they become unreferenced (I cannot judge whether this would be against the C++ standard or not).

initializer_list and argument-dependent lookup

I'm trying to use an std::initializer_list as an argument in a function that uses argument-dependent lookup (ADL). But I don't get it to work and I don't understand why. The following is a minimal failing example:
#include <initializer_list>
#include <iostream>
class Foo {
public:
inline friend void bar(std::initializer_list<Foo> v) {
std::cout << "size = " << v.size() << std::endl;
}
};
void baz(std::initializer_list<Foo> v) {
std::cout << "size = " << v.size() << std::endl;
}
int main(){
Foo a;
//bar({a,a}); // error: use of undeclared identifier 'bar'
baz({a,a}); // works
return 0;
}
As seen above, an equivalent global function works just fine. Why does the above not work?
I'm using clang on OS X 10.10.
I believe that the problem is that the subexpression1 { a, a } does not really have a type, and as such it does not have associated types or namespaces which in turn means that ADL does not kick in. If you have a function in the global namespace, normal lookup will find it, and it will find that { a, a } can match the function call as the initializer for std::initializer_list<Foo>.
1 The syntax { a, a } is called braced-init-list and it is not really an expression (or subexpression) in the language.
When the compiler sees bar({a,a}), it doesn't know the type of the arguments, so it searches for bar in the global namespace (::), and nowhere else. If you changed that line to Foo{a,a}, then it knows the argument is a Foo, and so also searches the Foo class for functions.

Template Run version Different from Debug

I have an object like the following
template<typename T>
inline void UnusedParameter( T const& )
{
}
class Client
{
public:
template<class T>
void runFFT(T *wSamples, float const &fMult)
{
std::cout << "INSIDE RUNFFT : : :" << std::endl;
UnusedParameter(wSamples);
UnusedParameter(fMult);
}
};
And in my CPP I have the following:
#include "object.hpp"
template<>
void Client::runFFT<int16_t>(int16_t *wSamples, float const &fMult)
{
std::cout << "INSIDE INT16_T version: : :" << std::endl;
UnusedParameter(wSamples);
UnusedParameter(fMult);
}
template<>
void Client::runFFT<Ipp32f>(Ipp32f *wSamples, float const &fMult)
{
std::cout << "INSIDE IPP32F version: : :" << std::endl;
UnusedParameter(wSamples);
UnusedParameter(fMult);
}
Both these implementation run without any problem in my Debug Code. It enters the int16_t version without problem and the Ipp32f version also without problem.
But when I try the Run version it only enters the Template, like the compiler only compiles the Template implementation in the header.
How do I prevent this from happenning? Shall I remove this and just create two different methods? I love my templates but these Heisenberg bugs are frustating.
Thanks for any input.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Andy Prowl answered this question and it is solved doing the following in the HPP:
template<typename T>
inline void UnusedParameter( T const& )
{
}
class Client
{
public:
template<class T>
void runFFT(T *, float const &)
{
// Thanks for Joachim for removing my Unused Parameter crap
std::cout << "INSIDE RUNFFT : : :" << std::endl;
}
};
template<>
void Client::runFFT<int16_t>(int16_t *wSamples, float const &fMult);
template<>
void Client::runFFT<Ipp32f>(Ipp32f *wSamples, float const &fMult);
And now it works in runtime as well. The CPP stays the same.
The problem is most likely in the fact that you relegated the specializations of your member function runFFT() for int16_t and Ipp32f in a separate .cpp file without providing a corresponding declaration after the primary template, so the compiler at the point of instantiation (which likely belongs to another translation unit that #includes only the header file containing the definition of Client) doesn't know about the existence of those explicit specializations.
Put a declaration for those specializations in the same header file that contains the definition of your class template:
template<typename T>
inline void UnusedParameter( T const& ) { }
class Client
{
public:
template<class T>
void runFFT(T *wSamples, float const &fMult)
{
std::cout << "INSIDE RUNFFT : : :" << std::endl;
UnusedParameter(wSamples);
UnusedParameter(fMult);
}
};
// DECLARE YOUR EXPLICIT SPECIALIZATIONS HERE
template<>
void Client::runFFT<int16_t>(int16_t *wSamples, float const &fMult);
template<>
void Client::runFFT<Ipp32f>(Ipp32f *wSamples, float const &fMult);
Per Paragraph 14.7.3/6 of the C++11 Standard:
If a template, a member template or a member of a class template is explicitly specialized then that specialization
shall be declared before the first use of that specialization that would cause an implicit instantiation
to take place, in every translation unit in which such a use occurs; no diagnostic is required. [...]
The "no diagnostic is required" part means that if you don't follow this rule, your program will be ill-formed, but your compiler/linker is not required to tell you. This normally results in the kind of undefined behavior you are observing.