I am observing behavior in the below code which I cannot readily explain and would like to understand the theory of better. I cannot seem to find an online documentation source or existing question which covers this particular situation. For reference, I am using Visual Studio C++ 2010 to compile and run the following code:
#include <iostream>
using namespace std;
struct Bottom_Class
{
template<typename This_Type>
void Dispatch()
{
// A: When this comment is removed, the program does not compile
// citing an ambiguous call to Print_Hello
// ((This_Type*)this)->Print_Hello();
// B: When this comment is removed instead, the program compiles and
// generates the following output:
// >> "Goodbye from Top Class!"
// ((This_Type*)this)->Print_Goodbye<void>();
}
void Print_Hello() {cout << "Hello from Bottom Class!" << endl;}
template<typename This_Type>
void Print_Goodbye() {cout << "Goodbye from Bottom Class!" << endl;}
};
struct Top_Class
{
void Print_Hello() {cout << "Hello from Top Class!" << endl;}
template<typename This_Type>
void Print_Goodbye() {cout << "Goodbye from Top Class!" << endl;}
};
template<typename Top_Type,typename Bottom_Type>
struct Merged_Class : public Top_Type, public Bottom_Type {};
typedef Merged_Class<Top_Class,Bottom_Class> My_Merged_Class;
void main()
{
My_Merged_Class my_merged_object;
my_merged_object.Dispatch<My_Merged_Class>();
}
Why does this work differently for the templated member function vs. non-templated member function cases ?
How does the compiler decide (in the templated case) that Top_Class::Print_Goodbye() is the appropriate overload rather than Bottom_Class::Print_Goodbye() ?
Thank you in advance for your consideration.
Both comments (AFAIK correctly) generate compilation error with GCC 4.6.3. May be the Microsoft compiler is doing something incorrect.
➜ scratch g++ -O2 templ.cc
templ.cc: In member function ‘void Bottom_Class::Dispatch() [with This_Type = Merged_Class<Top_Class, Bottom_Class>]’:
templ.cc:42:48: instantiated from here
templ.cc:16:9: error: request for member ‘Print_Goodbye’ is ambiguous
templ.cc:22:10: error: candidates are: template<class This_Type> void Bottom_Class::Print_Goodbye()
templ.cc:30:10: error: template<class This_Type> void Top_Class::Print_Goodbye()
In the Dispatch method, This_Type is the same as My_Merged_Class. The My_Merged_Class has two methods with the names of Print_Hello, of course the compiler is going to have problems to distinguish between them.
The call to Print_Hello in Dispatch, after template replacement, looks like this:
((My_Merged_Class*)this)->Print_Hello();
I hope the above substitution helps you see better why there is an ambiguity. The same problem should actually occur for Print_Goodbye, but it might be a bug in the compiler you are using that lets it through.
Related
I am building my program by using the latest Emscripten compiler.
It is based on Clang version 14. Actually it is a small test program which is the following:
#include <iostream>
struct Test {
template<typename T>
static inline void Dump(const T& value) {
std::cout << "[generic] = '" << value << "'\n";
}
template<>
static inline void Dump<std::string>(const std::string& value) {
std::cout << "[std::string] = '" << value << "'\n";
}
};
int main() {
std::string text = "hello";
Test::Dump(text);
return 0;
}
When I build it by Emscripten compiler I got the warning:
D:\em_test>emcc a.cpp
a.cpp:10:24: warning: explicit specialization cannot have a storage class
static inline void Dump<std::string>(const std::string& value) {
~~~~~~~ ^
1 warning generated.
If I just remove static keyword from void Dump<std::string> line
then there will be no warning. However, this code will cause compilation error in Visual Studio:
D:\em_test\a.cpp(17,11): error C2352: 'Test::Dump': illegal call of non-static member function
But this error is expected and clear.
I would like to write a cross-platform program.
So, I think I should simple disable this warning in Emscripten.
However, I can not find any Emscripten (which is based on clang version 14)
command line option for that!
And I am asking advice for that.
Actually I tried to use -Wno-static-inline-explicit-instantiation command line option but it did not help:
D:\em_test>emcc -Wno-static-inline-explicit-instantiation a.cpp
a.cpp:10:24: warning: explicit specialization cannot have a storage class
static inline void Dump<std::string>(const std::string& value) {
~~~~~~~ ^
1 warning generated.
However, I see in Clang version 13 user manual description about -Wstatic-inline-explicit-instantiation option but it is about a slightly another warning text.
Also it seems that Clang version 14 is not fully released, so, there is no public Clang version 14 user manual.
I can not find any Emscripten or Clang command line option to disable the above warning.
Could somebody help me?
Explicit specialization of (both static and non-static) function templates cannot be put into class definitions.
Just put it into the enclosing namespace(i.e somewhere after the class):
#include <iostream>
struct Test {
template <typename T>
static inline void Dump(const T& value) {
std::cout << "[generic] = '" << value << "'\n";
}
};
// Notice Test::
template <>
inline void Test::Dump<std::string>(const std::string& value) {
std::cout << "[std::string] = '" << value << "'\n";
}
int main() {
std::string text = "hello";
Test::Dump(text);
return 0;
}
inline is never necessary for in-class function definitions but it has different meaning for member variables.
inline for out-class is necessary in header files because the explicit specialization is not a template anymore.
When I use MSVC to compile this program, I receive the error,
"Example::bar ambiguous call to overloaded function"
Then, I found that the this keyword was able to resolve the error. Surprised, I used rextester and found that both Clang and GCC were able to compile the program without the this keyword.
Here is the program in question.
#include <iostream>
class Example {
public:
Example() {
auto lambda = [this]() {
//this->bar<int>(); // Using this allows the program to compile and run successfully.
bar<int>(); // This doesn't work in MSVC
};
lambda();
}
template<typename T>
void bar() {
std::cout << "(non-const) bar\n";
}
template<typename t>
void bar() const {
std::cout << "(const) bar\n";
}
};
int main() {
Example example;
}
Ultimately I am asking, is this keyword needed in a lambda to disambiguate between const and non-const member functions and whether MSVC is correct or GCC and Clang are correct.
I am using Visual Studio 2017 and the full version of MSVC is 191627027
Please take a look to this code snippet. I know it does not make much sense, it is just intended to illustrate the problem I am encountering:
#include <iostream>
using namespace std;
struct tBar
{
template <typename T>
void PrintDataAndAddress(const T& thing)
{
cout << thing.mData;
PrintAddress<T>(thing);
}
private:
// friend struct tFoo; // fixes the compilation error
template <typename T>
void PrintAddress(const T& thing)
{
cout << " - " << &thing << endl;
}
};
struct tFoo
{
friend void tBar::PrintDataAndAddress<tFoo>(const tFoo&);
private:
int mData = 42;
};
struct tWidget
{
int mData = 666;
};
int main()
{
tBar bar;
bar.PrintDataAndAddress(tWidget()); // Fine
bar.PrintDataAndAddress(tFoo()); // Compilation error
return 0;
}
The code above triggers the following error:
source_file.cpp:10:3: error: 'PrintAddress' is a private member of 'tBar'
PrintAddress(thing);
source_file.cpp:42:6: note: in instantiation of function template >specialization 'tBar::PrintDataAndAddress' requested here
bar.PrintDataAndAddress(tFoo()); // Compilation error
source_file.cpp:17:7: note: declared private here
void PrintAddress(const T& thing)
but only in Clang++. GCC and MSVC are fine with it (you can quickly test that by pasting that code in http://rextester.com/l/cpp_online_compiler_clang)
It seems as if tBar::PrintDataAndAddress<tFoo>(const tFoo&) is using the same access as tFoo, where it is befriended. I know this because befriending tFoo in tBar fixes this issue. The problem also goes away if tBar::PrintDataAndAddress is a non-template function.
I have not been able to find anything in the Standard that explains this behavior. I believe it could be a bad interpretation of 14.6.5 - temp.inject, but I can't claim I have read all of it.
Does anyone know if Clang is right failing to compile the above code? Can you please quote the relevant C++ standard text if that is the case?
It seems that for this problem to happen, the private member being accessed needs to be a template function. e.g., in the example above, if
we make PrintAddress a non-template function, the code will compile without errors.
Forcing the compiler to instantiate tBar::PrintDataAndAddress<tFoo> before using it solves the problem.
int main()
{
tBar bar;
bar.PrintDataAndAddress(tWidget()); // Fine
auto x = &tBar::PrintDataAndAddress<tFoo>; // <= make it work....
bar.PrintDataAndAddress(tFoo()); // Now fine
return 0;
}
It seems to be a compiler promlem as it looks quite similar to this:
In C++, why isn't it possible to friend a template class member function using the template type of another class?
To be a little bit more precise... In the line bar.PrintDataAndAddress(tFoo()); the compiler has to instanciate the memberfunction tBar::PrintDataAndAddress<tFoo> and at the same time it has to resolve the friend declaration. That are internaly two seperate steps. Apparent the compiler doesn't do it in the rigth order when written in one expression. To force the compiler to instantiate bar.PrintDataAndAddress(tFoo()) first by access to the function pointer these two steps are in the right order.
Try adding this before the friend function
template <typename tFoo>
I am facing compilation problem while running the following program:
I am calling non template member function inside template member function but getting the weird compilation errors.
#include <iostream>
#include <boost\shared_ptr.hpp>
class base
{
public:
base()
{
}
void fun2(boost::shared_ptr<int> &data)
{
std::cout << "This is fun2" << std::endl;
}
void fun3(boost::shared_ptr<double> &value)
{
std::cout << "This is fun3" << std::endl;
}
template <typename T>
void fun1(int switchParam,T &resonse)
{
std::cout << "This is fun1." << std::endl;
switch(switchParam)
{
case 0:
fun2(resonse);
break;
case 1:
fun3(resonse);
break;
}
}
};
void main()
{
boost::shared_ptr<int> myInt;
int switchParam = 0;
base b1;
b1.fun1(switchParam,myInt);
}
Getting the following compilation problem:
Error 1 error C2664: 'base::fun3' : cannot convert parameter 1 from 'boost::shared_ptr<T>' to 'boost::shared_ptr<T> &'
Any help will be appreciated.
No. You cannot do that. With second phase of template code compilation for any type the switch block has to be fully compiled by compiler. It will will fail to compile. You are mixing templates with runtime behavior of program. You better write a different function.
Note that switch is runtime, not compile time. When you call it as fun1(0) the compiler still has to compile it fully for int. It won't evaluate runtime switch statement and eliminate fun3, which takes shared_ptr<double>.
I have the following code (stripped down version from actual project to reproduce
the issue) that results in a compiler error on RHEL5 (g++ version 4.1.2):
----------- driver (test.cpp)--------------
#include <iostream>
#include <classa.hpp>
#include <func.hpp>
namespace globals {
static int kth(const A& a) {
return kth(a.ival());
}
}
using namespace globals;
int main() {
A a;
std::cout << func(a) << std::endl;
return 0;
}
----------class A (classa.hpp)------------
class A {
public:
A():val(0){}
const int ival() const {return val;}
private:
int val;
};
------- namespace globals (func.hpp) ------
namespace globals {
int kth(const int& c) {
return c;
}
template <class T>
int func(const T& key) {
return kth(key);
}
}
--------------------------------------------
Compiling it using g++ 4.1.2 gives me the following error:
func.hpp: In function ‘int globals::func(const T&) [with T = A]’:
test.cpp:15: instantiated from here
func.hpp:8: error: invalid initialization of reference of type ‘const int&’ from
expression of type ‘const A’
func.hpp:2: error: in passing argument 1 of ‘int globals::kth(const int&)’
Same code compiles and runs perfectly fine on RHEL4 (g++ version 3.4.6)! Any explanations/ideas/suggestions on how to resolve this error(?) on RHEL5 will
be much appreciated!
Edit:
Thanks Sergey. That is the obvious solution that I am aware of already. But I forgot to add that the restriction is that func.hpp cannot be edited (for e.g., its 3rd party write-protected). Any workarounds?
Here's what happens. When the function func() is defined, the compiler doesn't know about the function kth(const A&) yet because it is defined later in the code. So when it encounters a reference to kth() inside func(), it assumes that it is a reference to kth(const int&). Now when func() is actually instantiated, it fails to compile it because T is A, not int. I am not sure why it works in another version of the compiler, but I think it is because it actually starts resolving references when a template function is instantiated, not when it is declared. But this looks like a bug in the older version because with such behavior a function definition changes depending on where it is instantiated from, which is very confusing.
The only way to fix your code that it works with any compiler would be to put the definition of kth(const A&) between kth(const int&) and func() or a forward declaration of kth(const A&) somewhere above func().
Update
With the restriction of not editing func.hpp the best workaround I can think of is to create a custom header file with something like this:
#include <classa.hpp>
namespace globals {
static int kth(const A& a); // defined later, but used by func.hpp
}
#include <func.hpp>
I also don't see why kth(const A&) is defined as static, but used by a global header. I'd rather put it into the classa.cpp and its declaration into the classa.hpp. But this may be some design feature or artifact I am not aware of.