Deducing a shared base of two classes in C++ - c++

I am almost certain that what I'm looking for cannot be done without reflection, which is not yet in the language. But occasionally I'm getting surprised with exceptional answers in SO, so let's try.
Is it possible to deduce the "common_base" of two types that have a common shared base class, so the following would be possible (pseudo code! -- there is no common_base_t in the language, this is the magic I'm trying to achieve):
template<typename T1, typename T2>
const common_base_t<T1, T2>& foo(const T1& a1, const T2& a2) {
if(a1 < a2) return a1;
return a2;
}
Note that a1 and a2 above do not share a common_type, just by being siblings (sharing the same base) thus we cannot use the ternary operator.
Note also that changing the above return type to const auto& doesn't do the trick (it would not compile: inconsistent deduction for auto return type).
Here is a the naïve implementation, requiring the caller to state the expected return type:
template<typename R>
const R& foo(const auto& a1, const auto& a2) {
if(a1 < a2) return a1;
return a2;
}
Then we can call it with:
MyString1 s1 = "hello"; // MyString1 derives from std::string
MyString2 s2 = "world"; // MyString2 also derives from std::string
std::cout << foo<std::string>(s1, s2); // ok we return const lvalue ref
// pointing to one of the above objects
There are many reasons for why this probably cannot be achieved without providing the expected return value. But maybe it could be achieved somehow?

The standard library's std::common_reference<> is tantalizingly close to what you want, and arguably what your foo() function should be using, as it clearly expresses the desired semantics:
template<typename T1, typename T2>
std::common_reference_t<const T1&, const T2&> foo(const T1& a1, const T2& a2) {
if(a1 < a2) return a1;
return a2;
}
Unfortunately, it doesn't work out of the box for this specific use-case, as it cannot detect common bases unless one of the types derives from the other.
However, you can give it a hint by specializing std::common_type. Like so:
namespace std {
template<>
struct common_type<MyString1, MyString2> {
using type = std::string;
};
}
And it will "just work". You can see it in action here: https://gcc.godbolt.org/z/e3PrecPac.
Edit:
It's worth mentioning that, depending on your circumstances, you could also create a general purpose specialization of std::common_type for all types that derive from a given base:
struct SomeBase {};
namespace std {
template<std::derived_from<SomeBase> T1, std::derived_from<SomeBase> T2>
struct common_type<T1, T2> {
using type = SomeBase;
};
}
However, I would thread lightly with this. It's a potentially very broad and wide-ranging partial specialization. It could easily lead to ambiguities, especially if done more than once.

I can think of three approaches.
#1 is waiting for/using reflection.
#2 is using std::tr2::direct_bases and std::tr2::bases from way back. This is going to be a pain if you want to be able to handle "the common base is not the direct base, nor is it unique".
Doing this requires a metaprogramming library, and you end up with something like:
template<class Lhs, class Rhs>
struct common_base< Lhs, Rhs,
std::enable_if_t< always_true< extract_if_unique_t<common_direct_bases_t<Lhs, Rhs>> > >
> {
using type = extract_if_unique_t<common_direct_bases_t<Lhs, Rhs>>;
};
and it gets complex, with writing a bunch of metaprogramming boilerplate.
#3 is providing a canonical list of bases you are looking for as common bases, and searching for them as possible bases of your types. Often this is a good idea, as it means uninvolved implementation detail types don't derail you (the open-closed principle).
For the last one, I'd just do a is_base_of filter on the list of canonical bases in order for both types, then grab the first one in both lists.
template<template<class...>class Op, class List>
struct filter;
template<class Lhs, class Rhs>
struct intersect_lists;
template<class List>
struct front;
template<class...>
struct types {};
using canonical_bases = types<A,B,C,D>; // in order
template<class Derived>
struct BaseTest {
template<class Base>
using result = std::is_base_of_t< Base, Derived >;
};
template<class Lhs, class Rhs>
using canonical_common_base_of =
front_t< intersect_lists_t<
filter_t<BaseTest<Lhs>::template result, canonical_bases>,
filter_t<BaseTest<Rhs>::template result, canonical_bases>
>>;
with another few dozen lines of metaprogramming (or use an existing metaprogramming to reproduce something equivalent).

Related

How can I specialize std::common_type<A,B> so that it's naturally commutative?

std::common_type<T1, ..., TN> is a helper template in C++ which can find the common type which all of T1 ... TN are implicitly convertible to.
According the C++ spec, a user may specialize std::common_type<T1,T2> if certain conditions apply, and:
std::common_type<T1, T2>::type and std::common_type<T2, T1>::type must denote the same type.
However, common_type<T1, T2> might be a very complicated specialization for user types T1 and T2:
namespace std {
template <typename T1, complicated_constraint_of<T1> T2, ...>
struct common_type<complicated_expression_of<T1, ...>, complicated_expression_of<T2, ...>> {
using type = complicated_type_expression_of<T1,T2>;
};
}
In general, the constraint expressions are not necessarily symmetrical (for example, we might specify that T2 is a base of T1). This means that to preserve symmetry, we'd need to rewrite the entire specialization with T1 and T2 reversed, but doing that without making any mistake is extremely difficult and fragile.
How can I robustly define a commutative specialization of common_type<T1,T2> for my own types?
Here is the C++20 solution I came up with:
// define concept of `common_type<A,B>` existing
template <typename A, typename B>
concept has_in_common_ordered = requires { common_type<A,B>::type; };
namespace std {
// define common_type<A,B> if common_type<B,A>::type exists:
template <typename A, has_in_common_ordered<A> B>
struct common_type<A,B> : public common_type<B,A> {};
}
Now, with the above, the following should compile:
struct X {};
struct Y {};
namespace std {
template<>
struct common_type<X,Y> {
using type = X; // or whatever
};
}
int main() {
// even though we only specialized for one ordering,
// both orderings now exist:
std::common_type<X,Y>::type a;
std::common_type<Y,X>::type b;
}
I think one of two things is possible:
This is a natural technique that should really just be part of the standard definition of std::common_type.
This is extremely evil and you should never do it, for subtle reasons.
I await someone to tell me which one it is in the comments. ;)

c++ assigment operator with two or more template arguments

Code example :
#include <cstdio>
class Test
{
public:
template<typename T, typename T2>
bool operator =(T& value)
{
return true;
}
};
template bool Test::operator= <int, bool>(int&);
int main()
{
Test t;
int gg = 1234;
t = gg;
}
Checked several compilers. Problem with line t=gg.
If i remove second template argument T2, Everything compiles and works. Is it not allowed to have several template argumens for assigment operator?
You may use as many templates parameters as you need.
The problem is that the compiler does not have a means to deduce other template parameters except the template parameters that are specified in the declaration of the operator parameter or that have a default argument.
So you will have to write something like the following
#include <iostream>
class Test
{
public:
template<typename T, typename T2>
bool operator =( T& )
{
return true;
}
};
template bool Test::operator= <int, bool>(int&);
int main()
{
Test t;
int gg = 1234;
t.operator =<int, bool>( gg );
}
Is it not allowed to have several template argumens for assigment operator? [sic]
It's not that it's not allowed, but rather that the compiler has no way to deduce the second argument.
If your function looked something like this:
template<typename T>
bool operator =(T& value)
Then template parameter T could be deduced: It will be the type of whatever you're trying to set t to. Since this is all the template parameters, you're good.
However, what happens when you have 2?
template<typename T, typename T2>
bool operator =(T& value)
T is easily deducible, but what about T2? How would the compiler know what T2 should be? The answer is it can't.
You do have the option of telling it, by calling the operator function directly:
t.operator=<int, bool>(gg);
But I would imagine that wouldn't be what you want.
Unfortunately for you, the following expression won't work:
t =<bool> gg;
So I think calling operator=() directly is your only option here.
Imagine that in operator i create object of type T2 and put it some list. I did not show full source code, because problem can be seen from this part
It sounds like you're using the operator=() for something it wasn't designed to do. operator=() is really only supposed to be used for copying the other object's state. If everything you need to set your state isn't inherent in the T, then you probably shouldn't be using operator=() for this purpose.
Consider splitting it into two separate functions, or move it into a non-operator function altogether. This way, it will be more clear to other people reading your code in the future what it is you're doing anyway.
Disclaimer, I really don't understand what you want to achieve, and I absolutely agree wit hte others, that you're probably trying doing something with the assignment operator that it's not intender for.
But here's a piece of code that is at least syntactically close to your requirement. Instead of specifying the two parameters in the operator declaration, you could make Test a class template and use plain old function overloading.
template<typename T1, typename T2>
struct Test {
T2 operator=(T1&) { return {}; }
T1 operator=(T2&) { return {}; }
};
struct Foo {};
int main() {
Test<int, Foo> t;
int gg = 1234;
Foo x = t = gg; // <- this really looks weird, but works.
Foo ff;
int i = t = ff;
}
If this is not what you want, then we proably need more information about your comcreate use case.
Live code here.
It sounds like what you actually want to do is derive a second type from your first template argument. This would normally be achieved through some sort of traits type:
#include <cstdio>
template < typename T >
struct TestTraits;
template <>
struct TestTraits< int >
{
typedef bool T2;
};
class Test
{
public:
template<typename T>
bool operator =(T& value)
{
using T2 = typename TestTraits< T >::T2;
return true;
}
};
int main()
{
Test t;
int gg = 1234;
t = gg;
}

How to have ADL prefer a function template to another

I was wondering if it is possible to have ADL select the function template defined in the namespace of the class of one of the arguments (or in some other well defined place) in a situation when other function templates are visible. I have a motivating example that follows, and although I know the way around for that particular case (I discuss it below), the question in general seems to make sense.
I thought kind of cool to avoid using friend declarations but rather delegate work to methods, and thus came up with
namespace n
{
struct a
{
auto swap(a& a2) -> void;
};
auto swap(a& a1, a& a2) -> void
{
a1.swap(a2);
}
}
auto main(void) -> int
{
n::a a1, a2;
using std::swap;
swap(a1,a2); // use case 1
n::swap(a1,a2); // use case 2
}
So far, so good, both use cases work fine, but then, I added a second class with its own swap method and decided to save on boilerplate by turning the freestanding swap into a template:
namespace n
{
struct a
{
auto swap(a& a2) -> void;
};
struct b
{
auto swap(b& b2) -> void;
};
template<class T>
auto swap(T& t1, T& t2) -> void
{
t1.swap(t2);
}
}
auto main(void) -> int
{
n::a a1, a2;
using std::swap;
swap(a1,a2); // use case 1
n::swap(a1,a2); // use case 2
}
And here use case 1 breaks, the compiler complains about ambiguity with the std::swap template. If one anticipates the problem, it is possible to define swap functions rahter than methods (they will usually be friend, since they replace methods):
namespace n
{
struct a
{
friend auto swap(a& a1, a& a2) -> void;
};
struct b
{
friend auto swap(b& b1, b& b2) -> void;
};
}
Now everything works, so in the case of swap it is just enough to remember to use friend functions rahter than methods, but how about the general case? Is there any hack, however dirty, that would let the compiler unambiguously select n::foo<a> (or some other foo<a> under our control) in a situation where other template<class T> foo are visible, either in the global namespace or because of some using clause, especially if the latter are not ours to modify?
The culprit here is not just that you write using std::swap, but fundamentally that you have provided your own unrestricted function template swap that will give an overload resolution error with std::swap whenever namespace std is being considered during name lookup (either by an explicit using directive, or by ADL).
To illustrate: just leaving out the using std::swap will rescue you in this case
Live On Coliru
auto main() -> int
{
n::a a1, a2;
swap(a1,a2); // use case 1
n::swap(a1,a2); // use case 2
}
But suppose that you refactor your classes a and b into class templates b<T> and b<T>, and call them with a template argument from namespace std (e.g. std::string), then you get an overload resolution error:
Live On Coliru
#include <iostream>
#include <string>
namespace n
{
template<class>
struct a /* as before */;
template<class>
struct b /* as before */;
}
auto main() -> int
{
n::a<std::string> a1, a2; // oops, ADL will look into namespace std
swap(a1,a2); // use case 1 (ERROR)
n::swap(a1,a2); // use case 2 (OK)
}
Conclusion: if you define your own version of swap with the same signature as std::swap (as far as overload resolution is concerned), always qualify calls to it in order to disable ADL.
Tip: better yet, don't be lazy, and just provide your own swap function (not function template) for each class in your own namespace.
See also this Q&A where a similar mechanism is explained for why it is a bad idea to provide your own begin and end templates and expect them to work with ADL.
I know I must look silly to be answering my own question, but the fact of posting it, and the discussion, really brought some new understanding to me.
In retrospection, what should have struck me in the first place is the sequence
using std::swap;
swap(a1,a2);
It's so old-hat, and it clearly must be wrong, since using it repeatedly requires one to copy-paste the algorithm (of using using and then swapping). And you should not copy-paste, even if the algorithm is a two-liner. So what can be done better about it? How about turning it into a one-liner:
stdfallback::do_swap(a1,a2);
Let me provide the code that allows this:
namespace stdfallback
{
template<class T>
auto lvalue(void) -> typename std::add_lvalue_reference<T>::type;
template <typename T>
struct has_custom_swap
{
template<class Tp>
using swap_res = decltype(swap(lvalue<Tp>(),lvalue<Tp>()));
template <typename Tp>
static std::true_type test(swap_res<Tp> *);
template <typename Tp>
static std::false_type test(...);
static const bool value = decltype(test<T>(nullptr))::value;
};
template<class T>
auto do_swap(T& t1, T& t2) -> typename std::enable_if<has_custom_swap<T>::value,void>::type
{
swap(t1,t2);
}
template<class T>
auto do_swap(T& t1, T& t2) -> typename std::enable_if<!has_custom_swap<T>::value,void>::type
{
std::swap(t1,t2);
}
}
In the solution you find a SFINAE-based traits class has_custom_swap whose value is true or false depending on whether an unqualified call to swap for lvalues of the instantiation type is found (for that need the lvalue template, similar to declval but resolving to l-value rather than r-value), and then two overloads of a do_swap method for the case when the custom swap is present, and when it is not. They have to be called different than swap, otherwise the one calling the unqualified custom swap does not compile, because it is itself ambiguous to the swap it tries to call.
So maybe we should consider using this pattern instead of the established using?
(To give proper credit, the traits solution was inspired by http://blog.quasardb.net/sfinae-hell-detecting-template-methods/)

Is there any mechanism in (the future version of) C++ which can make those things realistic?

Suppose I have some template class called friendly_point, and a user-defined class called foo, and foo has some constructors with different arg number and types, eg, foo(),foo(int),and foo(int, char *).
I want to know if there is any method to implement friendly_point template so that I can write client code like this:
friendly_point<foo> obj1;
friendly_point<foo> obj2(1);
friendly_point<foo> obj3(1,"abc");
And even more, can friendly_point template be implementd in such a way that users of it can hook some memory pool to it, so all the objects it gives to the client comes form that pool?
You can already do this, just subclass a smart pointer and use perfect forwarding just like emplace already does. Example:
#include <memory>
#include <utility>
template<class T>
struct friendly_ptr
{
std::shared_ptr<T> p;
template<typename... Params>
friendly_ptr(Params&&... params) : p(new T(std::forward<Params>(params)...))
{
}
};
int main()
{
friendly_ptr<int> p(1);
}
C++11 introduces forwarding as illustrated by Mehrdad.
In C++03, you can try using templates ala:
template <typename T>
class Wrapper
{
public:
Wrapper() { }
template <typename T1>
Wrapper(const T1& t1) : t_(t1) { }
template <typename T1. typename T2>
Wrapper(const T1& t1, const T2& t2) : t_(t1, t2) { }
template <typename T1. typename T2, typename T3>
Wrapper(const T1& t1, const T2& t2, const T3& t3) : t_(t1, t2, t3) { }
...
private:
T t_;
};
Because the functions aren't checked for all errors until they're called, having placeholders for more arguments than T currently supports is ok.
You can use preprocessor macros to generate such forwarding functions for a generally ample but not insane number of arguments. You may or may not find it easier to do so with the boost preprocessor library.
While this often suffices in simple cases, it's not a general solution. Specifically - when you want to pass through a non-const parameter. You could:
leave const off in the argument list, but then non-const references can't bind to temporaries so the caller can't use a lot of normal expressions in preparing the parameter values, or
accept const references then cast away const-ness, but that removes a deliberate protective feature of the language and may permit attempts to change genuinely const variables, including e.g. string literals - resulting in undefined behaviour.

How can I pass an arithmetic operator to a template?

I want to somehow merge templates like these into one:
template <class Result, class T1, class T2>
class StupidAdd
{
public:
T1 _a; T2 _b;
StupidAdd(T1 a, T2 b):_a(a),_b(b) {}
Result operator()() { return _a+_b; }
};
template <class Result, class T1, class T2>
class StupidSub
{
public:
T1 _a; T2 _b;
StupidSub(T1 a, T2 b):_a(a),_b(b) {}
Result operator()() { return _a-_b; }
};
(followed by the same code for Mul, Div, etc) where all the code is the same,
except for the actual "+", "-" (and "StupidAdd", "StupidSub", etc).
These Stupid "functors" are then used by another template.
How can I avoid the repetition, WITHOUT the preprocessor?
(The reason I got into templates was to avoid the preprocessor)
That is, how can I pass arithmetic operators into a template?
Maybe you could use std::plus<T>, std::minus<T>, std::multiplies<T> and std::divides<T>. However, these will work only if both operands are of the same type, or probably if the left one can be converted to the type of the first one.
I don't see any way to achieve what you're trying to do, except by using the preprocessor. Any good reasons for not wanting macros ?
If you want to make sure the return type is large enough to contains the result, you could do something along this way:
#include <functional>
#include <boost/mpl/if_.hpp>
// Metafunction returning the largest type between T and U
// Might already exist in Boost but I can't find it right now...maybe
// boost::math::tools::promote_args
template <typename T, typename U>
struct largest :
boost::mpl::if_<
boost::mpl::bool_<(sizeof(T) > sizeof(U))>,
T,
U
>
{};
template <typename T, typename U, template <typename S> class Op>
struct Foo
{
typedef typename largest<T, U>::type largeType;
largeType bar(const T & t, const U & u)
{
return Op<largeType>()(t, u); // Applies operator+
}
};
int main()
{
Foo<int, double, std::plus> f;
double d = f.bar(12, 13.0); // takes int and double, returns double
}
Here, I used Boost MPL to write the largest metafunction, but you could write your own if metafunction if you cannot use Boost (class template parameterized by two types and a bool, specialized for true and false).
To determine the return type of an expression, you could also have a look at boost::result_of which, if I understand correctly, is equivalent to the upcoming decltype operator in C++0x.
Thanks Luc, this is very cool.
I finally did it in a simpler way:
#include <functional>
template <
class Result,
class T1,
class T2,
template <class ReturnType> class BinaryOp>
class Stupido
{
public:
T1 _a; T2 _b;
Stupido(T1 a, T2 b):_a(a),_b(b) {}
Result operator()() { return BinaryOp<Result>()((Result)_a,(Result)_b); }
};
And used the "plus", "minus" when instantiating Stupido.
The cast to "Result" was enough for my needs (int + double => double + double => double)
I think there's an improvement to OldCoder's solution:
#include <functional>
template <class Result,
template <class Result> class BinaryOp>
struct Stupido
{
template <typename T1, typename T2>
Result operator()(const T1& a, const T2& b) { return BinaryOp<Result>()((Result)a,(Result)b); }
};
This way the call can be made as:
Stupido<int, std::plus > stup;
int result = stup(3.0f, 2.0);
and the same function object can be used with multiple operands, so it could be passed in to a std::transform call.
I believe there must be a way to remove one Result from the template declaration, but I am unable to find it.
I'd use the C++ 0x decltype and the new definition of auto. You'll still need to define the classes, but using these, you can do a nicer job of defining them. They'll still be about as much work to define, but at least using them will be considerably cleaner. In particular, you can use decltype/auto to deduce the correct return type instead of having to specify it explicitly.
These are available with a fair number of recent compilers -- Intel C++, g++, Comeau, the beta of VC++ 2010, and even the most recent iteration of Borland/Inprise/Embarcadero/this week's new name.