C++ constructor template specialization - c++

I'm trying to create a specialized constructor for std::string arguments, but the other one is always used when I call it with a string argument.
struct Literal : Expression
{
template <typename V>
Literal(V val)
{
value = val;
}
};
template <>
Literal::Literal(std::string const& val)
{
value = val.c_str();
}
It doesn't matter if both are defined inside the class, both outside the class, or like in the posted example only the specialization is defined outside the class: When called with std::string, the assignment value = val gives a compiler error.
How do I correctly specialize this constructor template for std::string?

You don't.
You should overload the constructor: Literal(const std::string&), which you can do in the struct declaration.
The compiler always tries to match non-template overloads before template ones.

According to the standard, 14.8.2.1 Deducing template arguments from a function call [temp.deduct.call] where P is the template parameter and A is the function-call argument in that position:
2 If P is not a reference type:
If A is an array type, the pointer type produced by the array-to-pointer = standard conversion ([conv.array]) is used in place of A for type deduction; otherwise,
If A is a function type, the pointer type produced by the function-to-pointer standard conversion ([conv.func]) is used in place of A for type deduction; otherwise,
If A is a cv-qualified type, the top-level cv-qualifiers of A's type are ignored for type deduction.
If P is a cv-qualified type, the top-level cv-qualifiers of P's type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction. [...]
So given
std::string s{"hello"};
const std::string& sr{s};
Literal l(sr);
A (sr) is const std::string& but the constness is not considered, so the compiler considered std::string. This matches your
template <typename V>
Literal(V val)
{
value = val;
}
and so it uses this specialization. If you had specialized
template<>
Literal(std::string val)
the compiler would find this specialization, and this is probably what you will have to do and use move semantics.
#include <iostream>
#include <string>
struct S {
template<typename T>
S(T t) { std::cout << "T t\n"; }
std::string value_;
};
template<>
S::S(std::string value) {
std::cout << "string\n";
value_ = std::move(value);
}
template<>
S::S(const std::string&) {
std::cout << "const string&\n";
}
int main() {
S s1(42);
std::string foo{"bar"};
const std::string& foor = foo;
S s2(foo);
S s3(foor);
}
http://ideone.com/eJJ5Ch

Many times, when overload is a solution, people try to define full specialization. But overload could be a much better solution. In your case, I would create a new constructor with the string parameter.
Remember that only base template is considered in overload resolution. The following article is a good reference to understand this idea:
http://www.gotw.ca/publications/mill17.htm
UPDATE:
Anyway, to improve my answer, you could try the following base template constructor:
template <typename V>
Literal(V const& val)
{
value = val;
}

Related

How to define equivalent rule for non type template arg

After some time of figuring out my question I have found it's really fascinating how compiler can deduce template arguments from set of "tags" (non-type template args). But it looks like the compiler only understands "byte by byte" equality rule.
What I mean is this code:
struct S1 {
constexpr S1(int v) : _v(v)
{};
constexpr bool operator == (const S1& other) { return true;}
int _v;
};
template <S1 Tag, typename ValueType>
struct TagedValue { ValueType value; };
struct Test : TagedValue<S1{0}, int>, TagedValue<S1{1}, double> {};
template <S1 Idx, typename T>
auto& get(TagedValue<Idx, T>& test) {
return test.value;
}
int main()
{
Test test;
get<S1{1}>(test); // Still compiles, even though S1{0} == S1{1}
static_assert(S1{0} == S1{1});
}
As you can see I have defined my own operator == which basically says: "any two instances are equal". But it looks like in order to deduce T in get function the compiler still checks whether or not the actual content of the struct is the same. That is, for the purpose of type deduction S1{0} != S2{1}, even though S1{0} == S2{1} from "C++ point of view".
My question is: Is there any way I can redefine that "type deduction equality rule"?
UPD: To make it probably more clear, if I replace this line
struct Test : TagedValue<S1{0}, int>, TagedValue<S1{1}, double> {};
with
struct Test : TagedValue<S1{1}, int>, TagedValue<S1{1}, double> {};
the compiler gives an error, complaining about ambiguty.
Template parameter deduction, and other template-related things, use the concept of type equivalence. The parts relevant to your question are these:
temp.type/1 Two template-ids are the same if
...
(1.3) - their corresponding non-type template-arguments are template-argument-equivalent (see below) after conversion to the type of the template-parameter...
temp.type/2 Two values are template-argument-equivalent if they are of the same type and
...
(2.10) - they are of class type and their corresponding direct subobjects and reference members are template-argument-equivalent.
Roughly, it's member-wise equality. No, you cannot override it.
You can not override the equality check for template parameter objects:
An identifier that names a non-type template parameter of class type T denotes a static storage duration object of type const T, called a template parameter object, whose value is that of the corresponding template argument after it has been converted to the type of the template parameter. All such template parameters in the program of the same type with the same value denote the same template parameter object. A template parameter object shall have constant destruction.
https://en.cppreference.com/w/cpp/language/template_parameters

template specialization and rvalue reference, c++ [duplicate]

This question already has answers here:
Specializing function template for reference types
(3 answers)
Closed 6 months ago.
I'm a bit confused about this little example:
using mytype = std::vector<std::string>;
template<typename T>
void test(T item)
{
throw std::runtime_error(typeid(item).name());
}
template<>
void test(std::vector<std::string>&& vec)
{
std::cout<<"Ok."<<std::endl;
}
int main()
{
mytype stuff;
test(std::forward<mytype>(stuff));
}
I would expect the specialized template to be elected for the call here, but it's not, removing && will make that happen (and the argument is moved into vec)..
Why is the test version specialized for rvalue argument not being used?
Why is the test version specialized for rvalue argument not being used?
This is because the function argument std::forward<mytype>(stuff) that you're passing is an expression and an expression in C++ is never of some reference type. That is, the type of the function call argument std::forward<mytype>(stuff) is actually std::vector<std::string> and not std::vector<std::string>&&. In other words, T will be deduced as std::vector<std::string> and not std::vector<std::string>&&.
Basically, you've specialized the function template for the template argument std::vector<std::string>&& but T gets deduced to std::vector<std::string>. Thus, the specialization cannot be used. On the other hand, if you were to removed the && then the specialization will be called(see explanation at the end of the answer).
Lets look at a contrived example to clear this up:
Example 1
I am adding the following example to show that my above explanation is correct.
template <class T> void f(T)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void g() {
f((const int&)0); //T will be deduced as int and not "const int" or even "const int&"
f((int&&)0); //T will be deduced as int and not "int&&"
}
int main()
{
g();
return 0;
}
Working demo.
Example 2
template<typename T>
void test(T item) //#1
{
std::cout<<"generic"<<std::endl;
}
template<>
void test(int&& vec) //#2
{
std::cout<<"Ok."<<std::endl;
}
int main()
{
int stuff = 0;
//---vvvvvvvvvvvvvvvvvvvvvvvv----------->the argument is an expression and is of type int instead of int&&
test(std::forward<int>(stuff)); //calls #1
}
In the above example the expression std::forward<int>(stuff) is of type int and not int&&, therefore T is deduced as int(and not int&&). This means the generic version will be called.
removing && will make that happen
When you remove the &&, then this time you're explicitly specializing the function template for std::vector<std::string> and not std::vector<std::string>&&. This means that this time, the deduced T matches the template argument for which you've specialized the function template and so the specialization is called.
There are two problems here:
The first is that specialized functions do not participate in overload resolution.
The second is that std::forward does not convert your LValue to an RValue and correctly calls the generic function for type std::vector<std::string> resulting from the std::forward.
std::move takes an object of any type removes the reference and casts it as an rvalue reference
std::forward casts to the value category (lvalue or rvalue) the caller used to pass it.
To turn an LValue into an RValue you have to use move. And to make sure that your "specialized" function actually participates in the overload resolution, replace the specialization by a non-generic function overload like so:
template<typename T>
void test(T item)
{
throw std::runtime_error(typeid(item).name());
}
void test(std::vector<std::string>&& vec)
{
std::cout<<"Ok."<<std::endl;
}
int main()
{
std::vector<std::string> stuff;
test(std::move(stuff));
}

Using std::is_same with structural (non-type) template parameters

Consider a templated type containing a structural template parameter of any type. For the purpose of the example value_type<auto V> is defined.
We also declare a constexpr structure containing some member integral types with custom constructors that set the member values using a non-trivial expression (requiring more than memory copy).
Try feeding the structure into value_type and comparing it against another instance using std::is_same.
Example code to illustrate the case:
#include <iostream>
#include <type_traits>
template <auto V>
struct value_type
{
using type = decltype(V);
static constexpr type value = V;
};
struct hmm
{
//Both constructors set b=4 by default
constexpr hmm(int x, int y = 8) : a(x), b(y / 2) { }
constexpr hmm(float c, int z = 2) : a((int)c), b(z * 2) { }
const int a;
const int b;
friend constexpr bool operator==(const hmm& a, const hmm& b) { return false; }
friend constexpr bool operator!=(const hmm& a, const hmm& b) { return true; }
};
int main()
{
std::cout << (std::is_same_v<value_type<hmm(2)>, value_type<hmm(3.5f)>>) << ", ";
std::cout << (std::is_same_v<value_type<hmm(5)>, value_type<hmm(5.11112f)>>) << ", ";
std::cout << (std::is_same_v<value_type<hmm(5, 7)>, value_type<hmm(5.11112f)>>) << ", ";
std::cout << (std::is_same_v<value_type<hmm(5, 12)>, value_type<hmm(5.11112f, 3)>>) << std::endl;
return 0;
}
This code prints 0, 1, 0, 1 on gcc, msvc, and clang. It makes perfect sense, however, it got me wondering what are the limits of this mechanism.
How exactly is the comparison of those types performed?
Is this behavior standardized across compilers or is it just pure luck that they all seem to follow the same pattern here?
From what it looks like, their members are checked after construction, however apparently without using the comparison operators.
Is there a standard compliant way to override this comparison?
And, more generally, according to a potential is_same implementation using <T,T> specialization (source):
template<class T, class U>
struct is_same : std::false_type {};
template<class T>
struct is_same<T, T> : std::true_type {};
What are the rules for matching types T, U to be considered the same entity in the context of <T,T> specialization?
How exactly is the comparison of those types performed?
Is this behavior standardized across compilers or is it just pure luck that they all seem to follow the same pattern here?
As per NTTP on cppref, emphasis mine:
An identifier that names a non-type template parameter of class type T denotes a static storage duration object of type const T, called a template parameter object, whose value is that of the corresponding template argument after it has been converted to the type of the template parameter. All such template parameters in the program of the same type with the same value denote the same template parameter object. A template parameter object shall have constant destruction.
And type equivalence:
Template argument equivalence is used to determine whether two template-ids are same.
Two values are template-argument-equivalent if they are of the same type and
they are of integral or enumeration type and their values are the same
or they are of pointer type and they have the same pointer value
or they are of pointer-to-member type and they refer to the same class member or are both the null member pointer value
or they are of lvalue reference type and they refer to the same object -> - or function
or they are of type std::nullptr_t
or they are of floating-point type and their values are identical
or they are of array type (in which case the arrays must be member objects of some class/union) and their corresponding elements are template-argument-equivalent
or they are of union type and either they both have no active member or they have the same active member and their active members are template-argument-equivalent
or they are of floating-point type and their values are identical
or they are of non-union class type and their corresponding direct subobjects and reference members are template-argument-equivalent
There's no comparison but only type equivalence involved. If they have the same value, they all refer to the same object. That's it.
In your case, hmm(5) and hmm(5.11112f) refers to the same object, but not between hmm(2) and hmm(3).
Is there a standard compliant way to override this comparison?
I believe it's not allowed now.
What are the rules for matching types T, U to be considered the same entity in the context of <T,T> specialization?
They have the exact same type with cv-quailifiers.

Type deduction fails with pointer to member method

I have the following template class which acts as a proxy. It has a method named call which is supposed to be used to call methods on the wrapped object. There's a problem with it. The type deduction fails and I cannot understand why.
Hudsucker::f takes an std::string and then no matter if I pass an std::string or a const reference to it the compiler is able to call the right method.
But in case of Hudsucker::g with takes a const reference to std::string type deduction fails in both cases with both GCC and Clang.
GCC error for the first line:
main.cpp:36:28: error: no matching function for call to ‘Proxy<Hudsucker>::call(void (Hudsucker::*)(const string&), const string&)’
main.cpp:36:28: note: candidate is:
main.cpp:10:10: note: template<class A> void Proxy::call(void (T::*)(A), A) [with A = A; T = Hudsucker]
main.cpp:10:10: note: template argument deduction/substitution failed:
main.cpp:36:28: note: deduced conflicting types for parameter ‘A’ (‘const std::basic_string<char>&’ and ‘std::basic_string<char>’)
Especially this bit is strange: no matching function for call to Proxy<Hudsucker>::call(void (Hudsucker::*)(const string&), const string&). That is exactly the signature I would expect to see work.
Clang error for the first line:
main.cpp:36:7: error: no matching member function for call to 'call'
p.call(&Hudsucker::g, s); // <- Compile error
~~^~~~
main.cpp:10:10: note: candidate template ignored: deduced conflicting types for parameter 'A' ('const std::basic_string<char> &' vs. 'std::basic_string<char>')
void call(void (T::*f)(A), A a)
Code:
#include <string>
#include <iostream>
template <typename T> class Proxy
{
public:
Proxy(T &o): o_(o) {}
template <typename A>
void call(void (T::*f)(A), A a)
{
(o_.*f)(a);
}
private:
T &o_;
};
class Hudsucker
{
public:
void f(std::string s) {}
void g(std::string const &s) {}
};
int main()
{
Hudsucker h;
Proxy<Hudsucker> p(h);
std::string const s = "For kids, you know.";
std::string const &r = s;
p.call(&Hudsucker::f, s);
p.call(&Hudsucker::f, r);
p.call(&Hudsucker::g, s); // <- Compile error
p.call(&Hudsucker::g, r); // <- Compile error
return 0;
}
Could you explain why the type deduction fails in that way? Is there a way to get this to compile with const references?
The compiler cannot deduce the type A, since it has contrasting information. From the type of the member function, it would deduce A to be std::string const&, while from the type of the second argument, it would deduce it to be std::string.
Change your function template into one that allows different types for the parameter of the member function and the argument actually provided, and then SFINAE-constrain the latter to be convertible to the former:
template <typename A, typename B,
typename std::enable_if<std::is_convertible<B, A>::value>::type* = nullptr>
void call(void (T::*f)(A), B a)
{
(o_.*f)(a);
}
If you are wondering why from this function call:
std::string const s = "For kids, you know.";
// ...
p.call(&Hudsucker::g, s);
The compiler would deduce std::string, that's because of paragraph 14.8.2.1/2 of the C++11 Standard:
If P is not a reference type:
— If A is an array type, the pointer type produced by the array-to-pointer standard conversion (4.2) is
used in place of A for type deduction; otherwise,
— If A is a function type, the pointer type produced by the function-to-pointer standard conversion (4.3)
is used in place of A for type deduction; otherwise,
— If A is a cv-qualified type, the top level cv-qualifiers of A’s type are ignored for type deduction.
In the quoted paragraph, P is your A (from your function template) and A is std::string const. This means the const in std::string const is ignored for type deduction. To see this better, consider this simpler example:
#include <type_traits>
template<typename T>
void foo(T t)
{
// Does NOT fire!
static_assert(std::is_same<T, int>::value, "!");
}
int main()
{
int const x = 42;
foo(x);
}
Considering the second function call:
std::string const &r = s;
// ...
p.call(&Hudsucker::g, r);
The reason is that the type of the id-expression r is std::string const. The reference is dropped because of paragraph 5/5:
If an expression initially has the type “reference to T” (8.3.2, 8.5.3), the type is adjusted to T prior to
any further analysis. The expression designates the object or function denoted by the reference, and the
expression is an lvalue or an xvalue, depending on the expression.
And now we're back to the same situation as for the first function call.
As pointed out by Mike Vine in the comments, you may want to perfectly-forward your second argument when giving it in input to the first (member function) argument during the function call:
#include <utility> // For std::forward<>()
template <typename A, typename B,
typename std::enable_if<std::is_convertible<B, A>::value>::type* = nullptr>
void call(void (T::*f)(A), B&& a)
{
(o_.*f)(std::forward<B>(a));
}
If you cannot afford C++11, then you won't be allowed to use default arguments for template parameters. In that case, you can use the SFINAE-constraint on the return type:
template <typename A, typename B>
typename enable_if<is_convertible<B, A>::value>::type
// ^^^^^^^^^ ^^^^^^^^^^^^^^
// But how about these traits?
call(void (T::*f)(A), B a)
{
(o_.*f)(a);
}
Notice, that std::enable_if and std::is_convertible are not part of the C++03 Standard Library. Fortunately, Boost has its own version of enable_if and is_convertible, so:
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_convertible.hpp>
template <typename T> class Proxy
{
public:
Proxy(T &o): o_(o) {}
template <typename A, typename B>
typename boost::enable_if<boost::is_convertible<B, A>>::type
call(void (T::*f)(A), B a)
{
(o_.*f)(a);
}
private:
T &o_;
};
Notice, that boost::enable_if accepts as its first template argument a type which defines a value boolean member, whereas std::enable_if accepts a boolean value. The equivalent of std::enable_if in Boost is boost::enable_if_c.
Seems to me a simpler solution would be to just exclude one of the two arguments from trying to deduce A, and the second one is the better candidate:
template <typename A>
void call(void (T::*f)(A), typename std::identity<A>::type a)
{
(o_.*f)(a);
}
If you don't have std::identity in your type traits, use this one:
template <typename T>
struct identity { typedef T type; };
Here's why this works: the compiler cannot deduce A from the second argument, since it's just a template parameter to something that a nested type is taken of. Basically, it can't pattern-match any incoming type against something_that_contains_A::type - due to template specialization, it can't reverse-engineer the argument from the definition of the left side. The net result is that the second argument is an "undeduced context". The compiler will not attempt to deduce A from there.
This leaves the first argument as the only place where A can be deduced from. With only one deduction result for A, it is not ambiguous and deduction succeeds. The compiler then proceeds to substitute the deduction result into every place where A was used, including the second argument.
You just need to pass template argument to template function when calling it in your main.
int main()
{
Hudsucker h;
Proxy<Hudsucker> p(h);
std::string const s = "For kids, you know.";
std::string const &r = s;
p.call(&Hudsucker::f, s);
p.call(&Hudsucker::f, r);
//just add template argument to template function call !!!
p.call< const std::string & > (&Hudsucker::g, s); // <- NO Compile error !!!!
p.call< const std::string & > (&Hudsucker::g, r); // <- NO Compile error !!!**
return 0;
}

overload resolution in templates

Any one Explain this with an example
"Involving conversions on a function argumentsinvolved in template
parameter deduction."
EXamples like this:
template<class T> struct B { /* ... */ };
template<class T> struct D : public B<T> { /* ... */ };
template<class T> void f(B<T>&);
void g(B<int>& bi, D<int>& di)
{
f(bi);
f(di);
}
Please give me some more examples
EDIT: It is a point / statement from ISO C++ Standard 14.8.3/5th :Overload resolution
It is about what the example shows. In sum, these are those conversions
The function parameter can be a Base<T>, while the function argument is a Derived<T>. Compare with ifstream << "hello" - left side of operator<< is deduced in that way.
The function parameter can be a const U&, while the function argument is a U (same for volatile).
The function parameter can be a const U* or const E C::* while the function argument is a U* or E C::* respectively (these are the qualification conversions) - same for volatile.
Other conversions cannot be done for a function parameter / argument that participate in deduction. The whole range of conversions can be applied if a function parameter does not participate in deduction, but this needs a non-deduced context or a context where there is no argument to be deduced at all, and implementation don't actually agree on it (see here).
In other words:
template<typename T> struct id { typedef T type; };
template<typename T> void f(T, typename id<T>::type);
int main() {
// deduction acts on void(int*, int*) - deduction does not need
// to deduce anything and follows [temp.arg.explicit]p4
f<int*>(0, 0);
// deduction acts on void(T, id<T>::type) - second is a non-deduced context,
// which *will* allow the conversion of int -> int*, since it will not compare
// the argument with parameter during deduction (since it is non-deduced).
f((int*)0, 0);
}
The second example is vital for some partial ordering contexts to work.