I have a simple issue with ctor overload resolution for a class template:
#include <iostream>
#include <string>
using namespace std;
enum EnumTypeVal { READ, WRITE };
template <class T>
class TemplateClassTest {
public:
TemplateClassTest(const string & stringVal, T typeVal, EnumTypeVal e = READ,
const string & defaultStringVal = "default");
TemplateClassTest(const string & stringVal, const char * charVal);
TemplateClassTest(const string & stringVal, EnumTypeVal e = READ,
const string & defaultStringVal = "default");
private:
T type;
};
template <class T>
TemplateClassTest<T>::TemplateClassTest(const string & stringVal, T typeVal,
EnumTypeVal e,
const string & defaultStringVal)
{
type = typeVal;
cout << "In TemplateClassTest(const string &, T, EnumTypeVal, "
"const string &)" << endl;
}
template <class T>
TemplateClassTest<T>::TemplateClassTest(const string & stringVal,
const char * charVal)
{
cout << "In TemplateClassTest(const string &, const char *)" << endl;
}
template <class T>
TemplateClassTest<T>::TemplateClassTest(const string & stringVal, EnumTypeVal e,
const string & defaultStringVal)
{
cout << "In TemplateClassTest(const string &, EnumTypeVal, const string &)"
<< endl;
}
typedef TemplateClassTest<long long unsigned int> u32Type;
typedef TemplateClassTest<bool> boolType;
int main()
{
u32Type l("test", "0"); //matches ctor 2
u32Type v("test", 0); // ambiguity between ctor 1 and 2
boolType b("test", "true"); //matches ctor 2
return 0;
}
The second call fails to compile by throwing error:
Call of overloaded 'TemplateClassTest(const char [5], int) is ambiguous.
Why does int matches to const char *? This situation can be solved by changing the const char * to const string & in ctor 2. But doing so, boolType b("test", "true") now gets matched to ctor 1 instead of ctor 2.
My requirements are:
u32Type v("test", 0) should match ctor 1
boolType b("test", "true") should match ctor 2.
Limitations are:
ctor 1 and 3 signatures can't be changed
user code calls in main() can't be changed.
Any help is highly appreciated..Thanks!
It is ambiguous because 0 is a null pointer constant. A null pointer constant can be implicitly converted to any pointer type -> a null pointer conversion. Therefore, ctor 2 is viable: TemplateClassTest(const string&, const char*).
As an integer literal, 0 is of type int. Constructor 1, TemplateClassTest(const string&, T, EnumTypeVal = READ, const string & = "default"), therefore requires a conversion from int to unsigned long long -> an integral conversion.
Those two conversions, null pointer conversion and integral conversion, have the same rank (Conversion), hence the ambiguity.
[conv.ptr]/1
A null pointer constant is an integral constant expression prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion.
A possible, but ugly fix that matches your constraints is to change the second constructor to:
template<class U,
class V = typename std::enable_if<std::is_same<U, const char*>{}>::type>
TemplateClassTest(const string & stringVal, U charVal);
That is, a greedy constructor template, restricted by SFINAE to only accept const char* as the second argument. This heavily restricts the implicit conversions applied to the second argument when trying to match this ctor.
The out-of-line definition becomes:
template <class T>
template<class U, class V>
TemplateClassTest<T>::TemplateClassTest(const string & stringVal, U charVal)
{
cout << "In TemplateClassTest(const string &, const char *)" << endl;
}
Related
A string like "hello" would be string or const char*.
Consider an example:
template<typename A>
A s(A a){
// ...
}
here what would be "hello" converted to, if I called s("hello")?
A string like "hello" is a const char[6]. Since you can't pass an array by value, A will be deduced to const char* instead.
When you are looking for a type you may use this trick :
Create a struct without implementation
template<typename A>
struct Error;
And use it :
template<typename A>
A s(A a){
Error<A> error;
return a;
}
int main()
{
Error<decltype("error")> e; // error: implicit instantiation of undefined template 'Error<char const (&)[6]>'
s("hello"); // error: implicit instantiation of undefined template 'Error<const char *>'
}
The error will give you the type you are looking for.
Tada! "Hello" type is char const [6] but in the s the decuce type is const char *
Credit :
Effective Modern C++, Chapter 1. Deducing Types, Item 4: Know how to view deduced types.
https://www.oreilly.com/library/view/effective-modern-c/9781491908419/ch01.html
I'm playing around with overloading operators in c++14, and I tried to match two types of arguments: any-old-const-char*, and a-string-literal.
That is, I'm trying to see if I can discriminate between:
const char * run_time;
and
"compile time"
I wrote the code below, and as shown, when I try span >> "literal" it invoked the const char* function.
When I #if 0-out the const char* version, the template version gets called just fine.
If I change the template version to take an rvalue-reference (&&) parameter for literal, it doesn't compile.
If I add a const char (&literal)[] non-template version, the const char* version is still preferred. Removing the const-char* version, the template version is preferred.
Can you explain this? In particular:
Why is const char* preferred over const char (&)[N]?
Why is const char (&)[N] preferred over const char (&)[] (non-template)?
Why is const char (&&)[N] unable to compile?
Is there a "right way" to capture literal strings?
Thanks.
#include <iostream>
using namespace std;
#include <gsl/gsl>
#include <type_name.h++>
template<unsigned N>
auto
operator>>(gsl::span<const char*,-1>& spn, const char (&literal)[N])
-> gsl::span<const char*, -1>&
{
cout << "Got array: " << literal << endl;
return spn;
}
auto
operator>>(gsl::span<const char*,-1>& spn, const char *literal)
-> gsl::span<const char*, -1>&
{
cout << "Got const-char*: " << literal << endl;
return spn;
}
#if 0
#endif
int
main(int argc, const char *argv[])
{
auto spn = gsl::span<const char*>(argv, argc);
cout << type_name<decltype(spn)>() << endl; // gsl::span<const char *, -1>
cout << type_name<decltype("literal")>() << endl; // char const (&)[8]
cout << type_name<decltype(("literal"))>() << endl; // char const (&)[8]
auto helpx = "literal";
cout << type_name<decltype(helpx)>() << endl; // const char *
spn >> "literal"; // Got const-char*: literal
return 0;
}
Edit:
In case it matters, I'm compiling with:
c++ --std=c++14 -Iinclude -c -o main.o main.c++
And c++ says:
$ c++ --version
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin16.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Why is const char* preferred over const char (&)[N]?
The reason for this is rather technical. Even though the decay of a string literal from const char[N] to const char* is a conversion, it falls into the "lvalue transformation" category and is therefore considered by [over.ics.rank]/3 to be as good as no conversion at all. Since "no conversion" is required for either overload, the non-template overload wins.
Why is const char (&)[N] preferred over const char (&)[] (non-template)?
It is not possible to bind a reference to array of unknown bound to a value of type array of known bound. Instead, a reference to array of unknown bound can only be bound to values that are themselves arrays of unknown bound.
Why is const char (&&)[N] unable to compile?
A string literal is an lvalue so I'm not sure why you would expect this to work.
Is there a "right way" to capture literal strings?
You can use a helper function template that captures its argument using a forwarding reference so as to not destroy any type information (const char* versus const char[N]) then dispatch on the type using template specialization. You'll probably also want to use SFINAE to make sure it is disabled if anything other than a const char* or const char[N] is passed in. To wit,
template <bool b>
struct f_helper;
template <>
struct f_helper<true> {
void do_it(const char*) {
puts("pointer");
}
};
template <>
struct f_helper<false> {
template <std::size_t N>
void do_it(const char (&)[N]) {
printf("array of length %zd\n", N);
}
};
template <class T, class = typename std::enable_if<std::is_same<char*, std::decay_t<T>>::value ||
std::is_same<const char*, std::decay_t<T>>::value>::type>
void f(T&& s) {
f_helper<std::is_pointer<std::remove_reference_t<T>>::value>{}.do_it(s);
}
Coliru link: http://coliru.stacked-crooked.com/a/0e9681868d715e87
The overload taking a pointer is preferred because it is not a template according to
13.3.3 Best viable function [over.match.best]
Given these definitions, a viable function F1 is defined to be a better function than another viable function
F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
...
(1.7)
F1 is not a function template specialization and F2 is a function template specialization
actually non-template const char (&)[] does not seem to compile at all because it is a reference to a non-bound array. It is possible to pass a pointer like this const char [], but not array.
this should fail at least for the same reason as (2)
you can provide another template taking a reference to pointer:
template< typename = void > void
foo(char const * & text)
{
::std::cout << "got ptr" << ::std::endl;
}
Note that providing another template taking a pointer won't work because both template specializations will be fine and we'll get ambiguous choice of overloaded functions.
I am new to templates in C++.
Can anyone explain why my specialised constructor never gets executed.
It works when I remove the const and reference operator.
#include<iostream>
#include<string>
using namespace std;
template<typename T>
class CData
{
public:
CData(const T&);
CData(const char*&);
private:
T m_Data;
};
template<typename T>
CData<T>::CData(const T& Val)
{
cout << "Template" << endl;
m_Data = Val;
}
template<>
CData<char*>::CData(const char* &Str)
{
cout << "Char*" << endl;
m_Data = new char[strlen(Str) + 1];
strcpy(m_Data, Str);
}
void main()
{
CData<int> obj1(10);
CData<char*> obj2("Hello");
}
The output is
Template
Template
Because you cannot bind "Hello" to a const char*&.
The information dyp added in comments is quite interesting:
A string literal is an array lvalue, which can be converted to a pointer prvalue. A pointer prvalue cannot bind to a non-const lvalue reference like const char* &
Which means you can actually make it work by replacing const char*& by const char* const&, or even const char* && in c++11, not sure if this is really smart in your use case though.
UPDATE I got everything wrong, rewrote the answer completely.
First, this constructor
template<>
CData<char*>::CData(const char* &Str)
is not a specialization of CData(const T&), because the Str parameter here is a non-const reference to pointer to const char. So it's a definition of non-templated constructor CData(const char*&).
Second, "Hello" has type "array of n const char" (see What is the type of string literals in C and C++?) so it can't be converted to non-const reference. This is why "Template" constructor is called.
The correct specialization is
template<>
CData<char*>::CData(char* const& Str)
That is, it accepts a const reference to char*.
And after that you should remove CData(const char*&), unless you need for example this code to compile:
const char* foo = "foo";
CData<int> obj2(foo);
So here is the code:
template<typename T>
class CData
{
public:
CData(const T&);
private:
T m_Data;
};
template<typename T>
CData<T>::CData(const T& Val)
{
....
}
template<>
CData<char*>::CData(char* const& Str)
{
....
}
// warning: deprecated conversion from string constant to 'char*'
CData<char*> obj2("Hello"); // calls CData(char* const&)
The proper way of fixing above warning is to add another specialization:
template<>
CData<const char*>::CData(const char* const& Str)
{
...
}
CData<const char*> obj2("Hello"); // calls CData(const char* const&)
I have the following function
template <typename T, typename U>
const T* visitor_fct(U& operand)
{
return (boost::get<T>(&operand));
}
When I do
boost::variant<int, std::string> str = "string";
std::cout << visitor_fct<std::string>(str) << std::endl;
I get the correct output
But when I change the declaration of str to :
boost::variant<int, std::string, bool> str = "toto";
I am always getting a nullptr;
Why ?
The reason is that a string literal (char*)converts to bool better than to std::string so your string literal doesn't initialize the string component of the variant, but rather than bool component (to true).
See the following which outputs bool 1:
#include <iostream>
void foo(bool b)
{
std::cout << "bool " << b << std::endl;
}
void foo(std::string s)
{
std::cout << "string " << s << std::endl;
}
int main()
{
foo("Bar");
}
Initializing with std::string("toto") will solve your problem.
4.12/1 shows us the conversion in question:
A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a
prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false;
any other value is converted to true. A prvalue of type std::nullptr_t can be converted to a prvalue of
type bool; the resulting value is false.
[As noted also in the other answer] This implicit conversion takes precedence over the converting constructor of std::string and so is selected, causing the type used in the variant to be bool.
What seems to be happening here is because there's a bool present in the boost::variant, the const char* you're passing is no longer used for the string but rather is converted to bool.
if you change this line:
boost::variant<int, std::string, bool> str = "toto";
to this:
boost::variant<int, std::string, bool> str = std::string("toto");
it will work.
Here's why the bool gets chosen over the string: implicit conversions happen between any types of pointer and bool, and conversions between built-in types are always preferred to user-defined conversions. And since std::string is a user defined type (standard mind you but still user defined) the bool wins over the string.
#include <iostream>
using namespace std;
class Foo{
string _s;
public:
Foo(string ss){
_s = ss;
}
Foo& operator=(bool b){
cout << "bool" << endl;
return *this;
}
Foo& operator=(const string& ss){
cout << "another one" << endl;
return *this;
}
};
int main(){
Foo f("bar");
f = "this";
return 0;
}
I have overloaded = operator. I expected f = "this"; statement to call operator=(const string& ss) overload. But it does not. It calls operator=(bool b) overload. Why?
This operator operator=(const string& ss) requires a conversion of a user defined type for the argument (const char* to std::string), whereas the bool version has none and so provides a better match: you get the conversions from built-in types const char[5] to const char* to bool.
As noted by another answer, the pointer-to-char to bool conversion is preferred because it involves no user-defined conversions, while the std::string conversion has a user-defined conversion in it.
C++11 offers the ability to do manual type control.
template<size_t n>
struct enumarated_enum {
private:
enum empty {};
};
template<bool b, size_t n=0>
using EnableIf = typename std::enable_if< b, typename enumerated_enum<n>::empty >::type;
template<typename String, EnableIf< std::is_convertible< String, std::string >::value >... >
Foo& operator=(String&& s) {
cout << "string overload" << "\n";
}
// either this:
template<typename Bool, EnableIf< !std::is_convertible< Bool, std::string >::value && std::is_convertible< Bool, bool >::value, 1 >... >
Foo& operator=(Bool&& b) {
cout << "bool overload" << "\n";
}
// or this:
Foo& operator=(bool b) {
cout << "bool overload" << "\n";
}
where we match a type perfectly if it can be converted to a std::string, and the template doesn't match if it cannot be converted to std::string and other overloads are looked at.
If you have many types you want to be able to support, you have to use long logical forms that describe which of the overloads you want to run, and make sure the others don't accept it (with the not construct above). If you only have two types, the second type can be a traditional overload. The template overload gets first crack at anything that doesn't match the traditional overload exactly...
(The second template argument is a variardic list of numbered enums which cannot exist, whose only purpose is to make sure that the two templates differ in sufficient ways for the compiler not to complain.)
Without delving into C++ standards, on the surface your problem is, you defined your assignment overload with string& and a bool but in your test you're assigning a char* array.
So if you define a string and assign, it will work as expected.
Check it working here : http://codepad.org/owb6noXR
As others have said, an easy fix to this is to simply cast your string to an std::string, when doing the operator, so C++ knows exactly which overload to choose:
#include <iostream>
using namespace std;
class Foo{
string _s;
public:
Foo(string ss){
_s = ss;
}
Foo& operator=(bool b){
cout << "bool" << endl;
return *this;
}
Foo& operator=(const string& ss){
cout << "another one" << endl;
return *this;
}
};
int main(){
Foo f((string)"bar");
f = (string)"this";
return 0;
}