Template func with cond. const argument + template arg deduction - c++

I am implementing a wrapper class that wraps some base class object. I want the wrapper to be as unnoticeable as possible and therefore, I have already overloaded the -> operator to return a reference to the wrapped object in order to provide immediate access to the base-class's interface.
Additionally, I want my wrapper type to also implement all operators that the base-class implements and just delegates the respective operator calls to the wrapped object. However, since I want to be able to wrap any arbitrary base-class, I don't know a priori what operators are defined for the wrapped class.
My current approach consists of just defining all operators for my wrapper and using SFINAE to disable those implementations that are not covered by base-class implementations.
The implementation for the operator overload essentially always looks like this
auto operator <operator> (const Wrapper &lhs, const Wrapper &rhs) { return lhs.get() <operator> rhs.get(); }
auto operator <operator> (const Wrapper &lhs, const Base &rhs) { return lhs.get() <operator> rhs; }
auto operator <operator> (const Base &lhs, const Wrapper &rhs) { return lhs <operator> rhs.get(); }
where <operator> is the respective operator. Since, I don't want to duplicate this code for all operators, I have defined some macros to create the definitions for me.
This works for most operators, but now I also want to support the various assignment operators (e.g. *=). Here, the lhs argument must not be const as it is modified via the action of the operator.
I could just generate these separately, but I thought that there must be a way to simply make the lhs argument const or non-const depending on a constexpr boolean (available as macro-parameter). Thus, I created a templated helper cond_add_const< T, bool > that returns const T if passed true and T if passed false.
The problem now is, that the overloads that involve the Base class as direct parameters fail to be resolved due to template argument deduction failure. The problem seems to be that in order to apply my conditional const, I essentially have the replace the respective type with cond_add_const< T, true >::type and apparently everything to the left of :: does not participate in template argument deduction.
This seems rather frustrating and it also feels like for my simple case there should be a possibility to circumvent this limitation (or choose an approach that does not require it), but I just can't come up with one (that does not involve duplicating code). Any ideas?
MWE illustrating my problem:
#include <type_traits>
template< typename T, bool is_const > struct cond_add_const {
using type = typename std::conditional< is_const, typename std::add_const< T >::type, typename std::remove_const< T >::type >::type;
};
template< typename T > void myFunc(typename cond_add_const< T, true >::type lhs, int rhs) {
}
int main() {
myFunc(1, 2);
}
Compiling this snippet with g++ results in
test.cpp: In function ‘int main()’:
test.cpp:11:13: error: no matching function for call to ‘myFunc(int, int)’
11 | myFunc(1, 2);
| ^
test.cpp:7:29: note: candidate: ‘template<class T> void myFunc(typename cond_add_const<T, true>::type, int)’
7 | template< typename T > void myFunc(typename cond_add_const< T, true >::type lhs, int rhs) {
| ^~~~~~
test.cpp:7:29: note: template argument deduction/substitution failed:
test.cpp:11:13: note: couldn’t deduce template parameter ‘T’
11 | myFunc(1, 2);
|

You might turn your template functions in non-template friend function of your class:
template <typename T>
struct Wrapper
{
// ...
friend void myFunc(typename cond_add_const<Wrapper<T>, true >::type lhs, T rhs)
{ /*...*/ }
};
Demo
There are some caveats:
the friend function can only be found via "ADL" (So at least one parameter should be of the Wrapper type).

Related

What makes an overloaded function prefered to another?

I have implemented a simple BooleanVariable class, where I get the boolean value from template parameter V, and it's comparison function operator==:
#include <iostream>
template<bool V>
struct BooleanVariable {
BooleanVariable() = default;
constexpr bool operator()() const {
return V;
}
};
template<bool V>
bool operator==(const BooleanVariable<V> &, const BooleanVariable<V> &) {
return true;
}
template<bool V1, bool V2>
bool operator==(const BooleanVariable<V1> &, const BooleanVariable<V2> &) {
return false;
}
int main() {
std::cout<<(BooleanVariable<true>() == BooleanVariable<false>())<<"\n";
std::cout<<(BooleanVariable<false>() == BooleanVariable<true>())<<"\n";
std::cout<<(BooleanVariable<true>() == BooleanVariable<true>())<<"\n";
std::cout<<(BooleanVariable<false>() == BooleanVariable<false>())<<"\n";
return 0;
}
operator== works perfectly, that I wonder why these 2 comparison functions don't have ambiguity problem for the case when V1 == V2, as both of fit to the template. Why is
template<bool V1, bool V2>
bool operator==(const BooleanVariable<V1> &, const BooleanVariable<V2> &) {
return false;
}
not triggered when V1 == V2? Can someone explains to me?
A good rule of thumb for overload resolutions where templates are involved is that more specialized candidates are preferred over more generic candidates. For example:
void foo(int);
template <class T>
void foo(T);
If you call foo like foo(42), the non-templated version will be called since it is more specialized. If you call it with any other type, the templated version will be called.
Another example:
template <class T>
void foo(T, T);
template <class T, class Y>
void foo(T, Y);
Here, the second template is obviously more general. So, for cases where types of both arguments are the same, the first template will be preferred. For other cases, the second template will be preferred.
The same applies to your example. The first operator can only handle arguments of the same type while the second one can handle arguments of differing types as well as identical types. Thus, the first one will be called if possible. Otherwise the second one will be called.
In the case of the operator ==, both arguments are of (template) type BooleanVariable, no ambiguity. The two variants of operator && have one argument of a template type, and one of BooleanVariable. The first one has a BooleanVariable as the first arg, and the second variant has a BooleanConstant as the second type. That makes both definitions perfectly viable for being chosen and that's the ambiguity the compiler warns you about when you write BooleanVariable() && BooleanVariable(). If you look carefully both variants perfectly match the expression and does not something do differentiate them, This is not exactly the case of operator ==, though - here when both arguments of type BooleanVariable have their template non-type parameter with the same value the first variant is chosen, otherwise when they are different the second one (with two bool template non-type parameters) is chosen.

enable_if on conversion constructor (static cast, is_base_of)

I am working on an implementation of a shared pointer. (using C++17, in case it matters)
The only issue is the conversion constructor. I want to be able to static cast a smart_ptr to a smart_ptr of base type.
template<typename U>
inline smart_ptr(const smart_ptr<U>& rhs)
{
...
}
It works, but it will also try to cast a smart_ptr to a smart_ptr of any other type. For example, if I have an overloaded function that can take different kinds of smart_ptr's of unrelated type, I get a compiler error about ambiguous overload. So, I only want the conversion from smart_ptr -> smart_ptr if U is a derived class of T.
This looks like it should work. It compiles, but it does the opposite. It prevents the valid static upcasts from working, but still allows the casting to unrelated types:
template<typename U>
inline local_shared_ptr(typename enable_if<is_base_of<T,U>::value, const local_shared_ptr<U>&>::type rhs)
{
...
}
EDIT:
Got it working, thanks for the help. I choose jarod's solution, since I find template <typename U, enable_if_t<is_base_of<T,U>::value, int> = 0> the most concise. I didn't realize SFINAE could be that concise.
Also, since it was mentioned by Nathan:
Funnily enough, one of the issues I encountered is that I expected the template copy constructor to be called when the right-hand-side is the same type. Apparently, the compiler doesn't consider it an implementation of the copy constructor, and instead the auto-generated copy constructor was being called instead. Same issue for the move constructor and operator=. Not sure if that's a bug with MSVC2019.
U is non deducible with
template<typename U>
local_shared_ptr(enable_if_t<is_base_of<T,U>::value, const local_shared_ptr<U>&> rhs)
{
// ...
}
And as it is a constructor, you even cannot provide template explicitly.
So that constructor is useless.
You can use instead:
Default parameter (the most similar to your attempt IMO):
template <typename U>
local_shared_ptr(const local_shared_ptr<U>& rhs, enable_if_t<is_base_of<T,U>::value, int> = 0)
{
// ...
}
default template parameter (preferred way):
template <typename U, enable_if_t<is_base_of<T,U>::value, int> = 0>
local_shared_ptr(const local_shared_ptr<U>& rhs)
{
// ...
}
And as you use constructor, you cannot use return value.
Put the enable_if in the template parameter list like
template<typename U, std::enable_if_t<std::is_base_of_v<T, U>, bool> = true>
inline smart_ptr(const smart_ptr<U>& rhs)
{
}
And now this will only be called if U is T, or is derived from T. If you don't want to use this if U == T then you can use
template<typename U, std::enable_if_t<std::is_base_of_v<T, U> && !std::is_same_v<T, U>, bool> = true>
inline smart_ptr(const smart_ptr<U>& rhs)
{
}

Why won't this type conversion work? [duplicate]

I have a template class A
template <unsigned int m>
class A
{
public:
A(int) {}
};
Which has a constructor from int. And I have an operation:
template<unsigned int m>
A<m> operator+(const A<m>&, const A<m>&)
{
return A<m>(0);
}
But when I call:
A<3> a(4);
A<3> b = a + 5;
A<3> c = 5 + a;
I would like int to be implicitly converted to A, but compilers throws error.
Is there any elegant way to enable implicit conversion without using such solutions as:
a + A<m>(5)
operator+<3>(a, 5)
The solution is already shown in this answer. Now, more about the problem...
The problem in your code is how overload resolution is performed. When a template function is considered for overload resolution the compiler will perform type deduction on the arguments and come up with a type substitution that matches the call or else it fails to apply that template, removes it from the set of potential candidates and continues over. The problem at this point is that type deduction only deduces exact matches (with possible extra const/volatile qualification). Because the matching is exact, the compiler will not use any conversion (again, other than cv).
The simplest example of this happens with std::max and std::min functions:
unsigned int i = 0;
std::min( i, 10 ); // Error!
Type deduction will deduce the T in template <typename T> min( T const &, T const & ) to be unsigned for the first argument but int for the second they differ and the compiler will discard this template function.
The solution proposed in the answer is using a feature of the language that enables you to define a non-member friend function inside the class definition. The advantage with templates is that for every (different) instantiation of the template, the compiler will create a free non-template function at namespace level that has the signature obtained by substituting the real types of the instantiation in the friend declaration:
template <typename T>
class test {
friend test operator+( test const & lhs, test const & rhs ) { // [1]
return test();
}
}
test<int> t; // [2]
In the example above, the compiler allows you to add the definition of the friend function inside the class scope at [1]. Then when you instantiate the template in [2], the compiler will generate a free function:
test<int> operator+( test<int> const & lhs, test<int> const & rhs ) {
return test<int>();
}
The function is defined always, whether you use it or not (this differs to the template class member functions, that are instantiated on demand).
The magic here has multiple sides to it. The first part is that it generically you are defining non-template functions for each and all of the instantiated types, so you gain genericity and at the same time the advantage of overload resolution being able to use this function when the arguments are not perfect matches.
Because it is a non-template function, the compiler is able to call implicit conversions on both arguments, and you will get your expected behavior.
Additionally, a different type of magic goes on with lookup, as the function so defined can only be found by argument dependent lookup unless it is also declared at namespace level, which in our case cannot be done in a generic way. The implication of this might be good or bad, depending on how you want to consider it...
Because it can only be found by ADL it will not be considered unless at least one of the arguments is already of the desired type (i.e. it will never be used performing conversions to both arguments). The downside is that it is impossible to refer to the function unless you are actually calling it, and that means that you cannot obtain a function pointer.
(More on template friendship here, but note that in this particular case, all the other variants will fail to perform implicit conversions).
Every attempt to provide an operator using templates will need at least one second overload. But you can avoid that by defining the operator inside the class:
template <unsigned int m>
class A
{
public:
A(int) {}
inline friend A operator+(const A& a, const A& b) { return A(0); }
};
Works for both, a+5 and 5+a.
Add this operator
template<unsigned int m>
A<m> operator+(const A<m>&, const int&)
{
return A<m>(0);
}
OR try this
template <unsigned int m>
class A
{
friend const A operator+(const A& a, const A& b) { return A(0); }
public:
A(int) {}
// OR FOR UNARY
// const A operator+(const A &a) const {return A(0);}
};
int main(){
A<3> a(4);
A<3> b = a + 5;
A<3> c = 5 + a;
}
You could try adding an additional "policy" type argument to the template for your A class that will determine the actual desired conversion type. For instance:
template <unsigned int m, typename ConvVal = int>
class A
{
public:
typedef ConvVal conv_val;
A(ConvVal) {}
};
template<template <unsigned int, class U> class T, unsigned int m, typename U>
T<m, U> operator+(const T<m, U>&, const T<m, U>&)
{
return T<m, U>(0);
}
template<template <unsigned int, class U> class T, unsigned int m, typename U>
T<m, U> operator+(const T<m, U>&, const typename T<m, U>::conv_val&)
{
return T<m, U>(0);
}
template<template <unsigned int, class U> class T, unsigned int m, typename U>
T<m, U> operator+(const typename T<m, U>::conv_val&, const T<m, U>&)
{
return T<m, U>(0);
}
int main()
{
A<3> a(4);
A<3> b = a + 5;
return 0;
}
Now your A class will take an additional template argument that defaults to an int type, and it defines the actual type that you will allow automatic conversions from. You only need to overload the operator+ function three times, once for the version without the conversion value that will take explicit classes of type A<m, T>, and another for the two versions of operator+ that will take conversion types. In the above code I've generalized this with more generic types so that this can be done with pretty much any other class that has the proper template signature, and defines a conv_val typedef.

C++ concepts: Some signatures function conversion

Unfortunately, the only tutorial that I have found about concepts was the concept lite tutorial (and it was really basic). And even with the technical specification, there is some signatures function that I don't know how translate into concepts (maybe just because my English is bad and I can't read the technical specification really well).
So there is a list of signatures function I still don't know how to "translate":
CFoo --> class CFoo {};
void Foo1() const;
CFoo& Foo2();
void Foo3(CFoo&);
{static, friend, ... } void Foo4();
template < typename ... Args >
void Foo5(Args && ... args);
I want to have some kind of interface for a class with these functions.
Don't even know if it's possible at this point. Foo2 and Foo3 seems to be the same problem.
Honestly I really want to know Foo2 and Foo5.
I tried somethings, for the Foo2 but I don't have any idea about Foo5:
class Handle {};
template < typename Object >
concept bool C_Object =
requires(Handle handle) {
{get(handle)} -> Object&
};
template < C_Object Object >
class Foo {
Object obj;
};
int main() {
Foo<int> test;
return 0;
}
I know this won't compile because Foo don't have a get menber, but these are not the right errors:
Test1.cpp:6:16: error: there are no arguments to ‘get’ that depend on a template parameter, so a declaration of ‘get’ must be available [-fpermissive]
{get(handle)} -> Object&
^
Test1.cpp:6:16: note: (if you use ‘-fpermissive’, G++ will accept your code, but allowing the use of an undeclared name is deprecated)
Test1.cpp: In function ‘int main()’:
Test1.cpp:18:10: error: template constraint failure
Foo<int> test;
^
Test1.cpp:18:10: note: constraints not satisfied
Test1.cpp:4:14: note: within ‘template<class Object> concept const bool C_Object<Object> [with Object = int]’
concept bool C_Object =
^~~~~~~~
Test1.cpp:4:14: note: with ‘Handle handle’
Test1.cpp:4:14: note: the required expression ‘get(handle)’ would be ill-formed
If someone can point me out some resources or, why not, a solution. It will be great.
Have a great day
I know this won't compile because Foo don't have a get menber […]
Concepts deal with normal expressions. In particular, the scope of requires expressions is normal scope, not class scope. This may be more apparent with this concept:
template<typename Lhs, typename Rhs>
concept bool Addable = requires(Lhs lhs, Rhs rhs) {
lhs + rhs;
};
Addable<int, long> is fulfilled because given int lhs; long rhs; then lhs + rhs is a valid expression. We're using the built-in addition operator on two (pretend) variables we explicitly introduced in the parameter list, not calling a member operator+ on an implicit *this.
Concepts are about interfaces in the wider sense (as in 'API'), not in a narrower OOP sense. You can think of Addable as a relation on pairs of types. That Addable<int, long> holds doesn't mean int on its own has a special relationship with Addable. It is true that Addable can be used as e.g.
template<Addable<long> Var>
struct client {
Var var;
};
and then client<int> comes with an Addable<int, long> constraint, but this shortcut is syntactical in nature. It's a helpful way of cutting down boilerplate, namely sparing us from writing template<typename Var> requires Addable<Var, long>.
With that in mind, here are some expressions that may come close to checking the member signatures you mentioned, plus the Handle scenario:
template<typename Obj>
concept bool Object = requires(Obj obj, Obj const cobj, Handle handle) {
cobj.Foo1();
{ obj.Foo2() } -> Obj&;
obj.Foo3(obj);
// static
Obj::Foo4();
// non-member, possibly friend
Foo4(obj);
{ obj.get(handle) } -> Obj&;
};
(I elided the Foo5 scenario because it's worth its own question, here is a lead.)

Function overloads and inherited types are not calling the correct overload

Given the following base class:
struct ValueType {
String
ToString(String const& format) const;
};
I want this overload to be called for types deriving from ValueType:
String FormatValue(const ValueType& value, const String& format)
{
return value.ToString(format);
}
Otherwise, I want this overload to be called:
template <typename T>
String FormatValue(const T& value, const String& format);
How can I ensure that derived types do not instead call the second overload?
The original question is located here.
I am not too fond of what you are trying to do for different reasons (including the ValueType interface, why not use AnyToString always?), but at any rate you should be able to solve your problem with SFINAE
template <typename T>
typename enable_if< !is_base_of<ValueType, T>::value, String>::type
FormatValue( T const & value, const String& format ) { ... }
What that code does (once you make it compile :) is inhibiting the template function when the condition is met. When the compiler considers the template as an overload it will try to substitute the type, and the enable_if instantiation will fail to have a nested type if the condition is met, so the substitution fails and the template is discarded. After that, the best overload will be the version that takes a ValueType object.