From C++ Primer, I know that for template's parameter arguments, only two kinds of conversions are performed: one is const conversion, another one is array/function to pointer conversion.
However, when it comes to explicit argument, it seems that everything changes.
Assume we have a template function:
template <typename T>
int compare(const T &a, const T &b)
{
// do comparison
}
If no explicit argument involved, function call like this is illegal:
compare("foo", "foobar");
The weird thing happens (actually, it might not be weird but I do not understand) when we explicitly do:
compare<std::string>("foo", "foobar");
It seems that in the second call, "foo" and "foobar" are converted to std::string, which is controversial.
Is there any special rules for template explicit arguments? Thanks.
In the first case the compiler tries to deduce the type T from the given parameters. From the first parameter the compiler deduces the type const char (&)[4] (aka. reference to an array of 4 characters), from the second it gets const char (&)[7]. The two types don't match and the compiler can't figure out what T should be.
In the second example you explicitly specify that the T template parameter should be std::string. So T will be std::string. The compiler accepts the type you give and checks if the given function parameters match that type. In this case the parameters fit, because "foo" and "foobar" can be implicitly converted to std::string. (The const char[] degrade to const char*, and then there is a constructor that can construct a std::string from a const char*)
Related
Please refer to the following:
struct functorOverloaded
{
void operator()(const int& in_, ...) const {}
void operator()(short in_) {}
};
// helper to resolve pointer to overloaded function
template <typename C, typename... OverloadArgs>
auto resolve_overload(
std::invoke_result_t<C, OverloadArgs...> (C::* func)(OverloadArgs..., ...) const
)
{ return func; };
int main(int argc, char **argv)
{
using C = const functorOverloaded;
// works with exact function type
using myT = decltype(resolve_overload<C, const int&>(&C::operator()));
// can call with something convertible to const int&
static_assert(std::is_invocable_v<C,int>, "!!!");
// how to get the pointer to the overload that would be called when passed int (or double)?
// the next line doesn't compile (error C2672: 'resolve_overload': no matching overloaded function found)
using myT2 = decltype(resolve_overload<C, int>(&C::operator()));
return 0;
}
The above code allows retrieving a pointer to a specific overload of a function (operator() in this case), see here. One must know the exact argument type (const int&) in this case to get the pointer, even though i can just call the specific overload with a plain int, or even double. Is it possible to get a pointer to the overload that would be called with the specific argument (assuming the call is resolvable / not ambiguous)?
Edit: adding context:
I am writing a invocable_traits library for introspecting a callable. E.g., given a Callable, it will tell you the return type, arity and argument types, amongst some other properties. To support functors (including lambdas) with overloaded (or templated) operator(), the API of invocable_traits allows specifying call arguments to disambiguate which overload is to be used (or to instantiate the template). However, one must know the exact argument type (const int& in the example above), simply specifying int won't do in that case as there is no function with signature R operator()(int). Ideally, I'd like to allow discovering the signature of the exact overload/instantiation that gets called given the provided input argument types, ideally even taking into account any implicit conversions that are applied. Is this possible?
There is no way to get the function of an overload-set which would be called with the given arguments, unless you already know its signature.
And if you know, what's the point?
The problem is that for any given arguments, taking into account implicit conversions, references, cv-qualifiers, noexcept, old-style vararg, default arguments, and maybe also literal 0 being a null pointer constant, there are an infinite number of function-signatures which would match. And there is currently no facility for "just" listing all candidates.
I fixed a bug recently.
In the following code, one of the overloaded function was const and the other one was not. The issue will be fixed by making both functions const.
My question is why compiler only complained about it when the parameter was 0.
#include <iostream>
#include <string>
class CppSyntaxA
{
public:
void f(int i = 0) const { i++; }
void f(const std::string&){}
};
int main()
{
CppSyntaxA a;
a.f(1); // OK
//a.f(0); //error C2666: 'CppSyntaxA::f': 2 overloads have similar conversions
return 0;
}
0 is special in C++. A null pointer has the value of 0 so C++ will allow the conversion of 0 to a pointer type. That means when you call
a.f(0);
You could be calling void f(int i = 0) const with an int with the value of 0, or you could call void f(const std::string&) with a char* initialized to null.
Normally the int version would be better since it is an exact match but in this case the int version is const, so it requires "converting" a to a const CppSyntaxA, where the std::string version does not require such a conversion but does require a conversion to char* and then to std::string. This is considered enough of a change in both cases to be considered an equal conversion and thus ambiguous. Making both functions const or non const will fix the issue and the int overload will be chosen since it is better.
My question is why compiler only complained about it when the parameter was 0.
Because 0 is not only an integer literal, but it is also a null pointer literal. 1 is not a null pointer literal, so there is no ambiguity.
The ambiguity arises from the implicit converting constructor of std::string that accepts a pointer to a character as an argument.
Now, the identity conversion from int to int would otherwise be preferred to the conversion from pointer to string, but there is another argument that involves a conversion: The implicit object argument. In one case, the conversion is from CppSyntaxA& to CppSyntaxA& while in other case it is CppSyntaxA& to const CppSyntaxA&.
So, one overload is preferred because of one argument, and the other overload is preferred because of another argument and thus there is no unambiguously preferred overload.
The issue will be fixed by making both functions const.
If both overloads are const qualified, then the implicit object argument conversion sequence is identical, and thus one of the overloads is unambiguously preferred.
The function template std::transform() takes a range, operates on it component-wise with an operator, and saves the result in another range. In the following example, the function takes a generic std::initializer_list called nl and operates on it with (std::string (*)(T)) std::to_string to transform all of its entries into strings and then stores the result in an array of strings called buffer.
class num_list
{
public:
template<typename T>
num_list(std::initializer_list<T> nl):
size{nl.size()},
buffer(new std::string[size])
{
std::transform(nl.begin(), nl.end(), // input sequence
buffer, // output result
(std::string (*)(T))std::to_string); // unary operator
}
//...
private:
std::size_t size;
std::string * buffer;
};
I am wondering why we need to typecast std::to_string into a function pointer for this to work. Why does the code fail to compile with C++11 if we drop the casting to pointer-to-function type (std::string (*)(T))? I can't decipher the complain thrown by the compiler.
error: no instance of overloaded function "std::transform" matches the argument list
std::transform is a function template that takes the function object via a template parameter type. Since the type is a template parameter it must be deduced. std::to_string is a overloaded function so when you try to deduce its type, you get multiple results. Since there is nothing else in the deduction to help narrow down the type, the compiler will not be able to deduce the type of std::to_string and you'll get an error.
When you cast std::to_string to a std::string (*)(T), you now only have single type for the compiler to deduce, which it does, and you can compile successfully.
Instead of casting, you can use a generic lambda to forward to std::to_string instead like
std::transform(nl.begin(), nl.end(), // input sequence
buffer, // output result
([](auto&& val){ return std::to_string(val);});
but this requires at least C++14. For C++11 you can use T for the parameter type like
std::transform(nl.begin(), nl.end(), // input sequence
buffer, // output result
([](const T& val){ return std::to_string(val);});
I have a class that I have written which does type erasure. The public interface is:
template <typename T>
value(const T &t);
value(value &&v);
template <typename T>
operator T() const;
When I create a value instance from a std::string I have no problems, everything works as expected. When I try to get the std::string back out, using static_cast<std::string>(val), where val is an instance of value that is holding a std::string, I get the following error from VS2012:
error C2440: 'static_cast' : cannot convert from 'value' to std::string'
No constructor could take the source type, or constructor overload resolution was ambiguous
If I comment out the templated cast operator and add operator std::string() const then it compiles. I figure that something between the std::string constructors and the templated cast operator have the same goodness of match. Could anyone suggest what is happening and how to fix it?
Igor explained the problem. Here's my suggested solution:
Your class is obviously only intended to store one type of object at a time, so make that explicit. Replace the conversion functions with a real function:
template <typename T>
T get() const;
Now call it like this:
std::string myString = myValue.get<std::string>( );
No ambiguity. No chance of the wrong function being called and messing everything up. And I'd argue that it is now more readable.
std::string has several constructors capable of being called with one parameter - e.g. one taking const string&, and another taking const char*. What should T resolve to, then?
From the C++ standard:
5.2.9p4 Otherwise, an expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t.
In your case, the declaration std::string t(val); is ill-formed.
Following works with std::string (if you can return a reference).
static_cast<const std::string&>(val);
Given the code below, why is the foo(T*) function selected ?
If I remove it (the foo(T*)) the code still compiles and works correctly, but G++ v4.4.0 (and probably other compilers as well) will generate two foo() functions: one for char[4] and one for char[7].
#include <iostream>
using namespace std;
template< typename T >
void foo( const T& )
{
cout << "foo(const T&)" << endl;
}
template< typename T >
void foo( T* )
{
cout << "foo(T*)" << endl;
}
int main()
{
foo( "bar" );
foo( "foobar" );
return 0;
}
Formally, when comparing conversion sequences, lvalue transformations are ignored. Conversions are grouped into several categories, like qualification adjustment (T* -> T const*), lvalue transformation (int[N] -> int*, void() -> void(*)()), and others.
The only difference between your two candidates is an lvalue transformation. String literals are arrays that convert to pointers. The first candidate accepts the array by reference, and thus won't need an lvalue transformation. The second candidate requires an lvalue transformation.
So, if there are two candidates that both function template specializations are equally viable by looking only at the conversions, then the rule is that the more specialized one is chosen by doing partial ordering of the two.
Let's compare the two by looking at their signature of their function parameter list
void(T const&);
void(T*);
If we choose some unique type Q for the first parameter list and try to match against the second parameter list, we are matching Q against T*. This will fail, since Q is not a pointer. Thus, the second is at least as specialized as the first.
If we do the other way around, we match Q* against T const&. The reference is dropped and toplevel qualifiers are ignored, and the remaining T becomes Q*. This is an exact match for the purpose of partial ordering, and thus deduction of the transformed parameter list of the second against the first candidate succeeds. Since the other direction (against the second) didn't succeed, the second candidate is more specialized than the first - and in consequence, overload resolution will prefer the second, if there would otherwise be an ambiguity.
At 13.3.3.2/3:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [...]
S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form
defined by 13.3.3.1.1, excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence) or, if not that [...]
Then 13.3.3/1
let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the type of the i-th parameter of viable function F. 13.3.3.1 defines the implicit conversion sequences and 13.3.3.2 defines what it means for one implicit conversion sequence to be a better conversion sequence or worse conversion sequence than another.
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 [...]
F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.5.2, or, if not that, [...]
Finally, here is the table of implicit conversions that may participate in an standard conversion sequence at 13.3.3.1.1/3.
Conversion sequences http://img259.imageshack.us/img259/851/convs.png
The full answer is quite technical.
First, string literals have char const[N] type.
Then there is an implicit conversion from char const[N] to char const*.
So both your template function match, one using reference binding, one using the implicit conversion. When they are alone, both your template functions are able to handle the calls, but when they are both present, we have to explain why the second foo (instantiated with T=char const[N]) is a better match than the first (instantiated with T=char). If you look at the overloading rules (as given by litb), the choice between
void foo(char const (&x)[4));
and
void foo(char const* x);
is ambigous (the rules are quite complicated but you can check by writing non template functions with such signatures and see that the compiler complains). In that case, the choice is made to the second one because that one is more specialized (again the rules for this partial ordering are complicated, but in this case it is because you can pass a char const[N] to a char const* but not a char const* to a char const[N] in the same way as void bar(char const*) is more specialized than void bar(char*) because you can pass a char* to a char const* but not vise-versa).
Based on overload resolution rules (Appendix B of C++ Templates: The Complete Guide has a good overview), string literals (const char []) are closer to T* than T&, because the compiler makes no distinction between char[] and char*, so T* is the closest match (const T* would be an exact match).
In fact, if you could add:
template<typename T>
void foo(const T[] a)
(which you can't), your compiler would tell you that this function is a redefinition of:
template<typename T>
void foo(const T* a)
Cause " " is a char*, which fits perfectly to foo(T*) function. When you remove this, the compiler will try to make it work with foo(T&), which requires you to pass reference to char array that contains the string.
Compiler can't generate one function that would receive reference to char, as you are passing whole array, so it has to dereference it.