I have this very simple wrapper template:
template<class T>
struct wrapper {
inline operator T () {
return v;
}
inline wrapper(T v):v(v) { }
T v;
};
Trying to use it with any non-primitive type with (for example) comparison operator relying on the template having it defined doesn't look promising:
std::string t = "test";
assert(t == t);
typedef wrapper<std::string> string_wrapper;
assert(string_wrapper(t) == string_wrapper(t));
GCC 4.4.5 complains with this error:
error: no match for ‘operator==’ in ‘wrapper<std::basic_string<char> >(std::basic_string<char>(((const std::basic_string<char>&)((const std::basic_string<char>*)(& t))))) == wrapper<std::basic_string<char> >(std::basic_string<char>(((const std::basic_string<char>&)((const std::basic_string<char>*)(& t)))))’
What is interesting is that GCC triple-casts the template then fails to use operator == that was defined for std::string.
I don't think that implicit coercion is impossible, since if I change std::string to int or double, bool or anything primitive, GCC will choose the correct operator.
I do not want to define operator == for the wrapper struct, because that operator is just an example, and I need wrapper to 'feel' just like the real type regarding operators.
Just in cast GCC misunderstands my syntax, if I create a wrapper and try to compare it to itself, GCC complains again (though without triple casting) that it cannot find a matching == operator:
typedef wrapper<std::string> string_wrapper;
string_wrapper tw(t);
assert(tw == tw);
error: no match for ‘operator==’ in ‘tw == tw’
Why can't GCC find and/or use std::string operator == std::string when wrapper provides the cast?
That operator T is called a "conversion operator" or "conversion function" instead of a cast. "Conversions" are implicit; casting is explicit.
The compiler can't find the operator== for std::string because the overload resolution rules don't allow that to happen. More details about what you're really trying to do could help provide a solution.
Apparently this is a GCC 4.5 bug. The code is valid, unlike what Fred Nurk says.
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45383
The questioner has answered itself with some GCC PR, but which really does not answer his question.
The reason is like #Fred describes. Reducing it:
template<typename T>
struct A {
operator T() { return T(); }
};
int main() {
A<std::string>() == A<std::string>();
}
What can the compiler do? It could call operator std::string() on both operands and then do the comparison. But why should it do that call in the first place? It first needs to find an operator== that has two parameters of type std::string. Let's look at how its operator is defined
template<class charT, class traits, class Allocator>
bool operator==(const basic_string<charT,traits,Allocator>& lhs,
const basic_string<charT,traits,Allocator>& rhs);
There we have it. It first needs to do template argument deduction, and the fact that A does not match const basic_string<> will make it so that this operator== is ignored. You are lucky that operator== is found anyway using ADL so that it does argument deduction in the first place (since std::string is a template argument of your type, it will consider namespace std by ADL and find this operator).
So we have no suitable operator== to call and therefor GCC is alright with rejecting your code for the reasons #Fred gave in a nutshell. In the end, trying to make a class behave like another type is deemed to failure.
Related
I am learning about templates in C++, and came across an example where casting to void is used:
template<typename T>
auto func (T const& t) -> decltype( (void)(t.size()), T::size_type() )
{
return t.size();
}
In the explanation it is written that:
The cast of the expression to void is to avoid the possibility of a user-defined comma operator overloaded for the type of the expressions.
My question(s) is/are:
How can a cast to void be used to "avoid the possibility of a user-defined comma operator overloaded for the type of the expressions"? I mean, can anyone give any example where if we don't use void then this code would give an error? For example, let's say we have a class called SomeClass which has overloaded the comma operator. Now, can this become a problem if we don't use void?
Can static_cast be used in this case instead of a C style cast? For example, something like static_cast<void>(t.size()). I am reading examples that use C++17 features, and so I wonder why the author has used a C style cast in this case.
I have read What does casting to `void` really do?, from which I get the impression that if we use (void)x then this means to suppress compiler warnings, and also means "ignore the value of x". But then I can't understand the difference between the expression x and (void)x.
Consider this pathological type:
struct foo {
struct size_type {
bool operator,(size_type) { return false;}
};
size_type size() { return {};}
};
It does have a size_type and it does have a size() method. However, without the cast to void, the template does not deduce the right return type, because decltype( (t.size()), typename T::size_type() ) is bool :
#include <type_traits>
template<typename T>
auto func (T const& t) -> decltype( (t.size()), typename T::size_type() )
{
return t.size();
}
struct foo {
struct size_type {
bool operator,(size_type) { return false;}
};
size_type size() const { return {};}
};
int main()
{
func(foo{});
}
Results in error:
<source>:6:8: error: no viable conversion from returned value of type 'foo::size_type' to function return type 'decltype((t.size()) , typename foo::size_type())' (aka 'bool')
return t.size();
^~~~~~~~
<source>:20:4: note: in instantiation of function template specialization 'func<foo>' requested here
func(foo{});
^
1 error generated.
ASM generation compiler returned: 1
<source>:6:8: error: no viable conversion from returned value of type 'foo::size_type' to function return type 'decltype((t.size()) , typename foo::size_type())' (aka 'bool')
return t.size();
^~~~~~~~
<source>:20:4: note: in instantiation of function template specialization 'func<foo>' requested here
func(foo{});
^
A static_cast can be used. However, as nothing is actually being cast (it is an unevaluated context), the c-style cast does not do much harm. Note how by using the cast to void the user defined operator, is bypassed and the correct return type is deduced: https://godbolt.org/z/jozx1YGWr. This is because void, whatever uses the built-in operator, whose result is of same type as whatever. You cannot override void, whatever with a user-defined operator,; there is no syntax that works.
I suppose the code is merely to illustrate this one effect, because even with the cast to void one can make up other examples that fail (eg the template does not explicitly test for t.size() actually returning T::size_type).
See also the section on "Rarely overloaded operators" here:
The comma operator, operator,. Unlike the built-in version, the overloads do not sequence their left operand before the right one. (until C++17) Because this operator may be overloaded, generic libraries use expressions such as a,void(),b instead of a,b to sequence execution of expressions of user-defined types. The boost library uses operator, in boost.assign, boost.spirit, and other libraries. The database access library SOCI also overloads operator,.
Consider the following struct:
struct S {};
In C++14, the definition below is valid:
constexpr auto f() { return S{}, 'c'; }
As well as the following one:
constexpr auto f() { return S{}, void(); }
Now, consider the following, working snippet that involves the first of the two definitions:
#include<type_traits>
struct S {};
constexpr int operator,(S, char) { return 42; }
constexpr auto f() { return S{}, 'c'; }
int main() {
constexpr int i{f()};
static_assert(i == 42, "!");
static_assert(std::is_same<decltype(f()), int>::value, "!");
}
Speaking not so technically, the overload of the comma operator intercepts the couple S{}, 'c' and returns an integer, as correctly verified in the main function.
Now, suppose I want to do the same with the second definition of f:
constexpr auto f() { return S{}, void(); }
In this case, the comma operator should intercept the form S{}, void().
Neither the following definition works (for obvious reasons):
constexpr int operator,(S, void) { return 42; }
Nor the one below (that would have worked in the previous case):
template<typename T> constexpr int operator,(S, T &&) { return 42; }
Is there any way to overload the comma operator so as to deal with S{}, void()?
Isn't it otherwise a lack in the standard, for it allows to use the comma operator that way, but doesn't give you the chance to overload the same operator (even if the standard mentions that overloaded functions involving S are allowed)?
Note: this question is made for the sake of curiosity. Please, avoid comments like do not do that or it is not good practice. I'm not planning to do that in production environments. Thank you.
The relevant clause for this is 13.3.1.2/9 [over.match.oper] in N4140:
If the operator is the operator ,, the unary operator &, or the operator ->, and there are no viable functions,
then the operator is assumed to be the built-in operator and interpreted according to Clause 5.
As void() is never a valid function argument (see 5.2.2/7 [expr.call]), there never is a viable function and thus the built-in , will be used.
So no, what you are trying to do is not possible.
In fact, writing an iterator loop like this
for(...; ++it1, (void)++it2)
is a standard way to prevent users from breaking your code by overloading , for their iterator types by enforcing the built-in operator , to be used. (Note that I am not saying you need to do this in your everyday code. It very much depends on its actual use. This is standard library level of paranoid.)
Regarding the standard clause you linked:
The meaning of the operators =, (unary) &, and , (comma), predefined for each type, can be changed for specific class and enumeration types by defining operator functions that implement these operators.
But such a function cannot be defined because, as I said above, void() is never a valid function argument.
Now whether or not this is an oversight/problem in the standard is open to debate.
I currently have a map<int, std::wstring>, but for flexibility, I want to be able to assign a lambda expression, returning std::wstring as value in the map.
So I created this template class:
template <typename T>
class ValueOrFunction
{
private:
std::function<T()> m_func;
public:
ValueOrFunction() : m_func(std::function<T()>()) {}
ValueOrFunction(std::function<T()> func) : m_func(func) {}
T operator()() { return m_func(); }
ValueOrFunction& operator= (const T& other)
{
m_func = [other]() -> T { return other; };
return *this;
}
};
and use it like:
typedef ValueOrFunction<std::wstring> ConfigurationValue;
std::map<int, ConfigurationValue> mymap;
mymap[123] = ConfigurationValue([]() -> std::wstring { return L"test"; });
mymap[124] = L"blablabla";
std::wcout << mymap[123]().c_str() << std::endl; // outputs "test"
std::wcout << mymap[124]().c_str() << std::endl; // outputs "blablabla"
Now, I don't want to use the constructor for wrapping the lambda, so I decided to add a second assignment operator, this time for the std::function:
ValueOrFunction& operator= (const std::function<T()>& other)
{
m_func = other;
return *this;
}
This is the point where the compiler starts complaining. The line mymap[124] = L"blablabla"; suddenly results in this error:
error C2593: 'operator = is ambiguous'
IntelliSense gives some more info:
more than one operator "=" matches these operands: function
"ValueOrFunction::operator=(const std::function &other) [with
T=std::wstring]" function "ValueOrFunction::operator=(const T
&other) [with T=std::wstring]" operand types are: ConfigurationValue =
const wchar_t
[10] c:\projects\beta\CppTest\CppTest\CppTest.cpp 37 13 CppTest
So, my question is, why isn't the compiler able to distinguish between std::function<T()> and T? And how can I fix this?
The basic problem is that std::function has a greedy implicit constructor that will attempt to convert anything, and only fail to compile in the body. So if you want to overload with it, either no conversion to the alternative can be allowed, of you need to disable stuff that can convert to the alternative from calling the std::function overload.
The easiest technique would be tag dispatching. Make an operator= that is greedy and set up for perfect forwarding, then manually dispatch to an assign method with a tag:
template<typename U>
void operator=(U&&u){
assign(std::forward<U>(u), std::is_convertible<U, std::wstring>());
}
void assign(std::wstring, std::true_type /*assign_to_string*/);
void assign(std::function<blah>, std::false_type /*assign_to_non_string*/);
basically we are doing manual overload resolution.
More advanced techniques: (probably not needed)
Another approach would be to limit the std::function = with SFINAE on the argument being invoked is valid, but that is messier.
If you have multiple different types competing with your std::function you have to sadly manually dispatch all of them. The way to fix that is to test if your type U is callable with nothing and the result convertible to T, then tag dispatch on that. Stick the non-std::function overloads in the alternative branch, and let usual more traditional overloading to occur for everything else.
There is a subtle difference in that a type convertible to both std::wstring and callable returning something convertible to T ends up being dispatched to different overloads than the original simple solution above, because the tests used are not actually mutually exclusive. For full manual emulation of C++ overloading (corrected for std::functions stupidity) you need to make that case ambiguous!
The last advanced thing to do would be to use auto and trailing return types to improve the ability of other code to detect if your = is valid. Personally, I would not do this before C++14 except under duress, unless I was writing some serious library code.
Both std::function and std::wstring have conversion operators that could take the literal wide string you are passing. In both cases the conversions are user defined and thus the conversion sequence takes the same precedence, causing the ambiguity. This is the root cause of the error.
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);
I have seen other questions on SO regarding this, but none that explains it in full. What is the right ways for compilers to handle the two situations below? I have tried it with gcc 4.7.1 (with -std=c++0x), VS2010 and VS2012 an get different results on all:
Example 1:
struct BB
{
// generic cast
template<typename T>
operator T() const
{
return 0;
}
// string cast
operator std::string() const
{
return string("hello");
}
};
int main()
{
BB b;
string s = b;
}
Output:
gcc 4.7.1: Ok
VS2010: Ok
VS2012: Fail: "Cannot convert from BB to
string"
Example 2:
struct BB
{
// generic cast
template<typename T>
operator T() const
{
return 0;
}
// string cast
operator std::string() const
{
return string("hello");
}
};
int main()
{
BB b;
string s = (string)b;
Output:
gcc 4.7.1: Fail: call of overloaded string(BB&) is ambigious
VS2010: Ok
VS2012: Fail: "Cannot convert from BB to string"
Your second version with a C-style cast is ambiguous. The problem is that there are two ways that static_cast<std::string> can convert an instance of class BB to a string. The obvious path is by using your non-template std::string cast operator to convert directly to a string. There is a more devious path, and all but VS2010 have found it. This other path is to use your template cast operator to convert a BB to an allocator for a string and then construct an empty string using this allocator.
Template cast operators are potentially dangerous beasts. You have just given the compiler the tools to convert a BB to anything.
Your first version escapes this problem because std::basic_string(const _Alloc& __a) is an explicit constructor. Explicit constructors can be used by explicit conversions but not by implicit ones. It is this constructor plus the conversion to an allocator that creates the ambiguity, and this path cannot be used with an implicit conversion.
As to why VS1012 fails on the implied conversion, it could be a bug in VS2012, or it could be that C++11 creates even more avenues to get from a BB to a std::string. I am not a C++11 expert. I'm not even a novice.
One more item: your code would have failed even more miserably had you used
std::string s;
s = b;
The assignment operator in conjunction with the template conversion operator creates more ways to get from b to s. Should the system convert b to a std::string, to a char, or a char*?
Bottom line: You really should rethink your use of template conversion operators.
The reason is fails under some compilers is because they go to different lengths trying to figure out what you're doing. The culprit is the templated operator call.
As this SO question explains, templated operators have to be called explicitly (b.operator()<std::string> if you wanted to call template<typename T> operator();), but, there is no way to call your templated operator:
b.operator std::string()<std::string>; //invalid
b.operator std::string <std::string>(); //also invalid, string takes no template arguments
// a bunch more weird syntax constructions...
The issue comes from the fact that the name after operator depends on the template argument, so there is no way to specify it. gcc and VS2012 figured out what you were doing and noticed they can fit the conversion to either the template or the explicitly defined one => ambiguous call. VS2010 didn't do so and is calling one of them, you can find which one through debugging.
Template specialization could've helped in a case like this, however, trying to define
// string cast
template<>
operator std::string() const
{
return string("hello");
}
will fail, since the compiler can't tell the difference between that function and a regular one without the template<>. If there were some arguments in the prototype it would've worked, but operator typename does have any...
Long story short - avoid templated conversion operators.