Template Resolution in <Inside The C++ Object Model> - c++

In Chapter 7 of "Inside the C++ Object Model," it was written that the resolution of a nomember name depends on whether the use of name is related to "the type of parameter used to instantiate the template."
I write a test:
/// -------------Test.h---------------
#ifndef TEST_H
#define TEST_H
#include <iostream>
using namespace std;
extern double foo(double t);
template <typename T>
class Test {
public:
void fun1() {
member = foo(val);
}
T fun2() {
return foo(member);
}
private:
int val;
T member;
};
#endif
and
/// -------------test1.cc-------------
#include <iostream>
using namespace std;
double foo(double t) {
cout << "foo doule is called" << endl;
return t;
}
int foo(int t) {
cout << "foo int is called" << endl;
return t;
}
-------------test.cc--------------
#include "Test.h"
extern int foo(int t);
int main() {
Test<int> fi;
fi.fun1();
fi.fun2();
return 0;
}
I expect "foo double is called\n
foo int is called",
but I got "foo double is called\n
foo double is called".
My g++ version is bellow. I'll be appreciate it if you can help me.

I'm afraid the book is not painting the complete picture (and it's a bit aged). Yes, foo(member) is a function call that is dependent on the template parameter. But the specific way in which functions are looked up in templates is described in the C++ standard at [temp.dep.candidate]:
For a function call where the postfix-expression is a dependent name,
the candidate functions are found using the usual lookup rules
([basic.lookup.unqual], [basic.lookup.argdep]) except that:
For the part of the lookup using unqualified name lookup, only function declarations from the template definition context are found.
For the part of the lookup using associated namespaces ([basic.lookup.argdep]), only function declarations found in either
the template definition context or the template instantiation context
are found.
foo's overloads can be looked up in one of two ways. By direct unqualified lookup, and by argument dependent lookup (aka ADL). Simple unqualified lookup considers only the names that are known at the point of the template definition. Since you only declared foo(double), that is the only overload found at the point of the template definition.
At the point of instantiation, the compiler will try to do ADL to find more foo's, but fundamental types don't contribute to ADL. int cannot be used to find foo(int). So the compiler can do only one thing, convert the integer to a double, and call foo(double).
If you want to test your compilers ADL, you just need to add a simple user defined type and overload. For instance:
enum E{};
E foo(E) {
cout << "foo E is called\n";
return {};
}
int main() {
Test<E> fi;
fi.fun1();
fi.fun2();
return 0;
}

Related

Why does friend function found successfully via ADL

Consider the following code:
#include <stdio.h>
class A
{
public:
friend void foo(A a){ printf("3\n"); }
};
int main()
{
foo(A());
}
It works. But I thought that this code is invalid. It is because 3.4.1/3:
For purposes of determining (during parsing) whether an expression is
a postfix-expression for a function call, the usual name lookup rules
apply.
Usual name lookup rules could not find the friend function because name declared by friend is invisible in the global namespace in my case. Actually 3.3.1/4:
friend declarations (11.3) may introduce a (possibly not visible) name
into an enclosing namespace
This implies that the programm is ill-formed. It is because that there is no name which found during the determining is the expression foo(A()); is a postfix-expression for a function call.
I'm confusing...
When parsing the following program
#include <iostream>
using namespace std;
typedef int foo;
class A
{
public:
operator int(){
return 42;
}
};
int main()
{
cout << foo(A());
}
the output will be 42 because 3.4.1/3
For purposes of determining (during parsing) whether an expression is
a postfix-expression for a function call, the usual name lookup rules
apply.
that means: to determine if foo is a postfix-expression (e.g. a cast) or a function call, the compiler will first use name lookup and search for it in the global namespace and/or enclosing scopes / base classes (or with fully qualified lookups if available).
Now take this code:
#include <iostream>
using namespace std;
class A
{
public:
friend int foo(A a){ return 55; }
operator int(){
return 42;
}
};
int main()
{
cout << foo(A());
}
The above will output 55 thanks to ADL: foo will be found by searching inside the scopes defined by its potential arguments, i.e. A.
A friend declaration introduces a (possibly not visible) name as you posted (3.3.1/4)
friend declarations (11.3) may introduce a (possibly not visible) name
into an enclosing namespace
that means the following code will not work
#include <iostream>
using namespace std;
class A
{
public:
friend int foo(A a){ return 55; }
operator int(){
return 42;
}
};
int main()
{
cout << ::foo(A()); // Not found
cout << A::foo(A()); // Not found
}
You might want to search for "friend name injection" and/or the Barton-Nackman trick.
Short story: now ordinary lookups can't find friend declarations.
So the code you posted is well-formed because ADL allows it to run as I explained in the previous passages.

Overload for std::string not detected in template code

I'm writing some template code to determine if a given type can be passed as any argument to any available overload of a function. In the example below I've used the log function, but I've also tried this code on others in the math library, and the results are the same. The idea is to use function overloading and the sizeof operator to distinguish between cases where the type in question can legally be passed to the function in question (log, in this example).
If it worked, we'd have sizeof(overload<type>(NULL)) == sizeof(True) when 'type' can be legally passed to log, and sizeof(overload<type>(NULL)) == sizeof(False) otherwise. This does seems to work for most types, but fails for std::string.
Here's exactly how it fails:
Under normal circumstances we have sizeof(overload<std::string>(NULL)) == sizeof(False), as we should. But, when I declare an overload of log that does take a string, it still doesn't trigger the sizeof(True) branch of the logic. Note that I don't actually want to declare log(std::string) function, I'm just testing this code to make sure that it's able to detect all possible overloads.
At first I thought it just wasn't detecting overloads properly, but when I tried it with a user-defined class ('MyClass' in the example below), it worked fine: it produced sizeof(True) when log(MyClass) was declared, and sizeof(False) otherwise.
#include <iostream>
#include <math.h>
template<int>
struct TakesInt{};
struct True
{
};
struct False
{
// guarantees that sizeof(False) != sizeof(True)
True array[2];
};
// takes anything; fall back if no match can be found
template<typename T>
False overload(...);
// takes a specific type; does not actually call log
template<typename T>
True overload(TakesInt<sizeof(log(T()))>*);
// As a test, this is an overload of log that takes a string.
// I don't actually want to implement this, but it should make the compiler
// think that a string is a valid argument.
double log(std::string);
// a placeholder for user defined class; could really be anything,
// like an arbitrary number class
struct MyClass{};
// declaring log for the arbitrary class above
// note that this is the same as for the log(std::string)
// if one works, the other should
double log(MyClass);
int main()
{
std::cout << sizeof(True) << '\t' << sizeof(False) << std::endl;
std::cout << sizeof(overload<std::string>(NULL)) << std::endl;
std::cout << sizeof(overload<double>(NULL)) << std::endl;
std::cout << sizeof(overload<MyClass >(NULL)) << std::endl;
return 0;
}
Here's the same issue w/o the SFINAE distraction:
#include <iostream>
namespace ns
{
struct string {};
}
void bar(...) { std::cout << "void bar(...)\n"; }
template<class T>
void foo()
{
T x{};
bar(x);
}
void bar(ns::string) { std::cout << "void bar(ns::string)\n"; }
int main()
{
foo<int>();
foo<ns::string>();
}
Output:
void bar(...)
void bar(...)
Lookup of a dependent function name will be performed:
as (pure) unqualified lookup from the point of definition
as (pure) argument-dependent lookup from the point of instantiation
Therefore, the following example differs:
#include <iostream>
namespace ns
{
struct string {};
}
void bar(...) { std::cout << "void bar(...)\n"; }
template<class T>
void foo()
{
T x{};
bar(x);
}
namespace ns
{
void bar(ns::string) { std::cout << "void bar(ns::string)\n"; }
}
int main()
{
foo<int>();
foo<ns::string>();
}
Output:
void bar(...)
void bar(ns::string)
For std::string, the only associated namespace is std. The global namespace is not associated and will not be searched in the OP's code. Therefore, the overload declared after the template definition will not be found.
N.B. Please do not inject overloads into namespace std. This will lead to undefined behaviour as per [namespace.std]/1.

Function overload resolution trouble

Over the weekend, I had specific problem with function overload resolution that I can't seem to solve.
The code below is a distillation of the problem:
#include <iostream>
using namespace std;
template<typename T>
void f(const T& t)
{
cout << "In template function." << endl;
}
class C
{
public:
void f() { cout << "In class function." << endl; }
void g() { int i=0; f(i); }
void h() { int i=0; f<int>(i); }
void i() { extern void f(int); int i=0; f(i); }
};
int main()
{
cout << "Test" << endl;
C c;
c.i();
return 0;
}
1) C::g won't compile because the compiler won't try the template. It just complains that there is no C::f to match.
2) C::h won't compile for no reason that is obvious to me. The message is "expected primary-expression before 'int'"
3) C::i will compile, but (after commenting out g and h) it won't link to anything. I think I understand this: the extern is forcing the linker to look into another compilation unit, but any template definition would be in this compilation unit.
I would appreciate any clarification on the reasons for 1 and 2. Also, ultimately, can someone suggest a way to get this to work that doesn't involve creating another compilation unit?
Likely it is finding C::f instead of global f. Use ::f<int>(i).
Look here: http://ideone.com/zs9Ar
Output:
Test
In template function.
#include <iostream>
using namespace std;
template<typename T>
void f(const T& t)
{
cout << "In template function." << endl;
}
class C
{
public:
void f() { cout << "In class function." << endl; }
void g() { using ::f; int i=0; f(i); }
};
int main()
{
cout << "Test" << endl;
C c;
c.g();
return 0;
}
Names in a smaller scope hide names in an outer scope.
Use ::f to indicate global namespace.
In i() you declare a function called f, but indeed there is no definition (it is not the same as the template), so you get a linker error.
C++ is somewhat "greedy" when trying to find functions. As soon as it finds a matching function name in the current scope (in this case, class C) it stops and then tries to match the parameters. If the parameters don't match, it won't try additional scopes. I assume this was to keep compiler writers sane. That's why g doesn't compile.
The same thing applies to h. It sees a non-template function f in the class scope and gets confused. It thinks you're trying to do a < comparison against the function C::f.
In both cases you can use ::f to qualify the name to the global scope.
Functions with the same name will hide other functions of the same name when they have different scopes, even if they have different signatures.
Instead of using a template for the first f, you could just do f(int) and the result would be the same for #1(the C::f() hides the ::f(int)).
One solution is to add "::" before the name to force the global namespace.
Another solution would be to change the names.

How to write a different version of stl algorithms in c++?

For practice, I wrote some template functions whose names are the same as the stl algorithms. But my code can not compile
error: Call to < algorithm_name > is ambiguous.
I only included using std::necessary_names; in my code rather than using namespace std;.
Usually when you have using, the "used" name takes precedence:
namespace N { int x = 0; }
int x = 1;
int main() {
using N::x;
cout << x;
}
// Output: 0
However, Argument-Dependent Lookup can mess this up:
namespace N {
struct T {};
void f(T) {}
}
namespace M {
void f(N::T) {}
}
int main() {
using M::f;
N::T o;
f(o); // <--- error: call of overloaded 'f(N::T&)' is ambiguous
}
So, if you are having trouble, qualify your own namespace (in this example, M) explicitly:
namespace N {
struct T {};
void f(T) { cout << "N::f"; }
}
namespace M {
void f(N::T) { cout << "M::f"; }
}
int main() {
using M::f;
N::T o;
M::f(o); // <--- Output: "M::f"
}
In a somewhat bizarre twist, you can also use parentheses to prevent ADL:
namespace N {
struct T {};
void f(T) { cout << "N::f"; }
}
namespace M {
void f(N::T) { cout << "M::f"; }
}
int main() {
using M::f;
N::T o;
(f)(o); // <--- Output: "M::f"
}
Explanation
[n3290: 3.4.1/1]: [re: unqualified name lookup] In all the cases
listed in 3.4.1, the scopes are searched for a declaration in the
order listed in each of the respective categories; name lookup ends
as soon as a declaration is found for the name. If no declaration is
found, the program is ill-formed.
[n3290: 3.4.1/2]: [i.e. first priority] The declarations from the
namespace nominated by a using-directive become visible in a namespace
enclosing the using-directive; see 7.3.4. For the purpose of the
unqualified name lookup rules described in 3.4.1, the declarations
from the namespace nominated by the using-directive are considered
members of that enclosing namespace.
[n3290: 3.4.2/1]: [re: argument-dependent lookup] When the postfix-expression in a function call
(5.2.2) is an unqualified-id, other namespaces not considered during
the usual unqualified lookup (3.4.1) may be searched, and in those
namespaces, namespace-scope friend function declarations (11.3) not
otherwise visible may be found. These modifications to the search
depend on the types of the arguments (and for template template
arguments, the namespace of the template argument).
i.e. Normal lookup stops at the name that you brought into scope with using, but when ADL comes into play, other names are also added to the candidate set, causing an ambiguity between two names.
It's better to declare your own version in a namespace; so that such problem would not occur.
namespace MySTL
{
template<typename T, ... > // ... means other template params
class vector;
template<typename T, ... >
class queue;
...
}
now you can do,
using std::vector;
which will not collide with MySTL::vector.
Chances are that you are hitting a problem with Argument Dependent Lookup. When you pass a type that is defined within a namespace to an unqualified function, the namespaces of all the arguments are implicitly added to the lookup set, and that might cause collisions. You can try to qualify your own algorithm calls to inhibit ADL from kicking in.
namespace n {
struct test {};
void foo( test const & ) {}
};
int main() {
n::test t;
foo( t ); // Will find n::foo as the argument belongs to n namespace
}

Weird behaviour of Koenig Lookup

consider the following program:
namespace NS2 {
class base { };
template<typename T>
int size(T& t) {
std::cout << "size NS2 called!" << std::endl;
return sizeof(t);
}
};
namespace NS1 {
class X : NS2::base { };
}
namespace NS3 {
template<typename T>
int size(T& t) {
std::cout << "size NS3 called!" << std::endl;
return sizeof(t) + 1;
}
template<typename T>
class tmpl
{
public:
void operator()() { size(*this); }
};
};
int main() +{
NS3::tmpl<NS1::X> t;
t();
return 0;
}
My compiler (gcc 4.3.3) does not compile the program because the call to size is ambigous. The namespace NS2 seems to be added to the set of associate namespaces for the size call in the class tmpl. Even after reading the section about Koenig Lookup in the ISI Standard I am not sure if this behaviour is standard conform. Is it?
Does any one know a way to work around this behaviour without qualifying the size call with the NS3 prefix?
Thanks in advance!
Template arguments and base classes both affect ADL, so I think GCC is correct, here: NS3 comes from the current scope, NS1 from the X template argument, and NS2 from the base class of the template argument.
You have to disambiguate somehow; I'd suggest renaming one or more of the functions, if feasible, or perhaps use SFINAE to disambiguate the functions.
(Similar Situation: Note that boost::noncopyable is actually "typedef noncopyable_::noncopyable noncopyable;" so that the boost namespace doesn't get added to the ADL set of types that derive from it.)