Short general question:
Is there a way to provide a default "fallback" overload of a function template? I read about some techniques here on Stack Overflow, but one requires using a variadic function (f(...)) which has some serious drawbacks in my case, and other requires listing all the type combinations that are overloaded, which is too verbose and not automated enough for my needs. This question was asked, but it's a few years old now, so I was wondering if there are some new solutions to this using the features of the latest standards and maybe even some solutions that will be possible in C++20 using concepts.
Long detailed question:
I'm trying to implement something like dynamic typing in C++. The point is, that I have several "abstract" types like Bool, Integer, Decimal, String etc. that are represented with some built-in types i.e. Integer is stored as a long long, but any integral type except bool is converted to it.
Now I started to implement operators. I could implement every possible overload (i.e. Bool + Integer, Integer + Integer, ...) manually, but I'd like to have only one implementation per some "category" i.e. Decimal + any_lower_type_than<Decimal> (commutative), where any_lower_type_than<Integer> refers to certain hierarchy of the abstract types. I have all the needed metafunctions to distinguish the categories and hierarchy implemented. I then use SFINAE with those metafunctions to provide the definitions. For example:
// decimal + lower, commutative:
template <typename first_t, typename second_t,
std::enable_if_t<commutative_with_lower<first_t, second_t, Decimal>::value>* = nullptr>
Decimal operator+ (const first_t& first, const second_t& second) {
return first + second;
}
// string * lower than integer, commutative:
template <typename first_t, typename second_t,
std::enable_if_t<commutative_with_lower_than<first_t, second_t, String, Integer>::value>* = nullptr>
String operator* (const first_t& first, const second_t& second) {
String string_arg = is_string<first_t>::value ? first : second;
auto other_arg = is_string<first_t>::value ? second : first;
String result = "";
for (decltype(other_arg) i = 0; i < other_arg; ++i)
result += string_arg;
return result;
}
So that's all OK, but what I need now is some default "fallback" implementation, that gets called as a last resort, when no match was found. So I need to somehow force the fallback implementation to be the worst possible match, but at the same time be a match for any case.
I read about the variadic function "sinkhole" being a worse match than anything. So the natural approach would be to provide the fallback as operator+ (...), which is impossible of course. So I changed all the operators to regular functions e.g. operator_add and then implemented the operator+ itself at a higher level by calling the appropriate operator_add overload, where the default overload is implemented as a variadic function operator_add(...). But then I encountered another problem. Variadic functions accept only trivial types. But I need to use this technique for user-defined types as well.
So the question is: Is there any alternative technique to the variadic function sinkhole that would ensure the worst possible match? Is there maybe even a way to do this without modifying the functional arguments, maybe by modifying the template arguments, so that I could use the technique on actual operators, that have fixed functional arguments signature?
Add another parameter to your operator_adds, including the fallback.
Give the fallback's extra parameter a different type (say, long), than the rest (say, int).
Make your operator+ call it with an int argument so that the fallback has a worse conversion.
So the question is: Is there any alternative technique to the variadic function sinkhole that would ensure the worst possible match?
You might use hierarchy to order overloads:
template <std::size_t N> struct overloadPriority : overloadPriority<N -1> {};
template <> struct overloadPriority<0>{};
then
template <typename T>
std::enable_if_t<MyTrait5<T>::value> foo_impl(T&&, overloadPriority<5>) {/*..*/}
template <typename T>
std::enable_if_t<MyTrait4<T>::value> foo_impl(T&&, overloadPriority<4>) {/*..*/}
// ...
template <typename T>
void foo_impl(T&&, overloadPriority<0>) {/*..*/} // Fallback
template <typename T>
auto foo(T&& t)
{
return foo_impl(std::forward<T>(t), overloadPriority<42>{});
// number greater or equal to max overload_priority used by that overload.
}
You could simply define your fallback to have a trailing parameter pack:
template <typename A, typename B, typename... T>
whatever_t operator_add(A&& a, B&& b, T...)
{
…
}
Overloading will always prefer non-templates over templates and prefer a more specialized template to a less specialized template. Based on [temp.deduct.partial]/11, if two function templates are otherwise equally well-suited the one with a trailing parameter pack loses.
The only downside of this approach is that there will be potential issues should you ever plan to add overloads of operator_add() that take more than two arguments. Also, should someone accidentally pass more than two arguments, the fallback would be called and just swallow them. Unfortunately, due to the issue mentioned in the comments by Jarod42, we cannot simply use std::enable_if to remove the overload in case there actually happen to be arguments for the parameter pack (like I originally proposed). One thing you could do to at least guard against the accidental call is just add another overload that would be called in this case and define it as deleted:
template <typename A, typename B, typename C, typename... T>
void operator_add(A&&, B&&, C&&, T...) = delete;
Related
Consider any of the common type-level algorithms provided by libraries such as Boost.MP11, Brigand, etc...
For instance:
template<typename... Args>
struct TypeList;
using my_types = TypeList<int, float, char, float, double>;
constexpr int count = boost::mp11::mp_count_if<my_types, std::is_floating_point>::value;
// this holds:
static_assert(count == 3);
Notice that std::is_floating_point could be defined as:
template<typename T>
struct is_floating_point { constexpr bool value = __compiler_magic(T); };
And likewise, we have the std::floating_point concept
template<typename T>
concept floating_point = requires (T t) { __other_compiler_magic(T); };
Sadly, despite the similarity, there does not seem to be an easy way to write something like this without introducing a manually-named wrapper for the concept:
constexpr int count = boost::mp11::count_if<my_types, std::floating_point>::value;
My question is: why cannot concepts be passed in place of types at this point ? Is it a lack of standardization, or is it something that these libraries can solve by providing more overloads ?
It looks like every concept has to be wrapped in a templated type which will just call the concept on its template argument.
From the outside, concepts just look like meta-functions whose domain is {set of types} -> bool. Compilers are able to delay passing parameters to "traditional" type-based metafunctions such as std::is_floating_point, why can't the same seem to happen with concepts ?
The literal answer is that we have template template parameters but not concept template parameters, so you can't pass a concept as a template argument.
The other literal answer is that it was never part of the original concepts proposal and nobody has put in the effort to suggest it as an extension (although I've been collecting use-cases).
One thing that would have to be answered is how dependent concepts affect subsumption - since currently use of concepts is never dependent and so figuring out subsumption is straightforward (actually, it's still not straightforward at all, but at least all the things you need are right there). But in a scenario like:
template <template <typename> concept C, typename T>
requires C<T>
void foo(T); // #1
template <typename T>
void foo(T); // #2
Probably if #1 is viable, you want to say it's a beter candidate than #2 since it's still constrained while the other is not. Maybe that's trivial. But then:
template <template <typename> concept C, typename T>
requires C<T>
void bar(T); // #3
template <OtherConcept T>
void bar(T); // #4
Let's say #3 and #4 are both viable, is it possible to say which is better? We generally say a whole overload is always better than a different one - but that might not be the case here. Maybe this is just ambiguous?
That seems to me like the main question that would need to be answered in order to get concept template parameters.
The other question might be, can I write foo<convertible_to<int>>(42). convertible_to<int> isn't really a unary concept, but it is a type-constraint that is treated as one in certain contexts, so I would still expect that to work.
Once we have such a thing, I'm sure Boost.Mp11 will quickly acquire something like:
template <template <typename...> concept C>
struct mp_quote_c {
template <typename... T>
using fn = mp_bool<C<T...>>;
};
So that you can write:
constexpr int count = mp_count_if_q<my_types, mp_quote_c<std::floating_point>>::value;
Consider following code:
int64_t signed_vector_size(const std::vector v){
return (int64_t)v.size();
}
This does not work since std::vector is a template. But my function works for every T!
Easy fix is to just do
1)
template<typename T>
int64_t signed_vector_size(const std::vector<T>& v){
return (int64_t)v.size();
}
or make the template implicit
2)
int64_t signed_vector_size(const auto& v){
return (int64_t)v.size();
}
Or concept based solution, option 3.
template<class, template<class...> class>
inline constexpr bool is_specialization = false;
template<template<class...> class T, class... Args>
inline constexpr bool is_specialization<T<Args...>, T> = true;
template<class T>
concept Vec = is_specialization<T, std::vector>;
int64_t signed_vector_size(const Vec auto& v){
return (int64_t)v.size();
}
I like the second solution, but it accepts any v, while I would like to limit it to the vector type only. Third is the best when just looking at the function, but specifying concepts is a relatively a lot of work.
Does C++20 syntax has any shorter way for me to specify that I want any std::vector as an argument or is the 1. solution the shortest we can do?
note: this is silly simplified example, please do not comment about how I am spending too much time to save typing 10 characters, or how I am sacrificing readability(that is my personal preference, I understand why some people like explicit template syntax).
A template is just a pattern for something. vector is the pattern; vector<int, std::allocator<int>> is a type. A function cannot take a pattern; it can only take a type. So a function has to provide an actual type.
So if you want a function which takes any instantiation of a template, then that function must itself be a template, and it must itself require everything that the template it takes as an argument requires. And this must be spelled out explicitly in the declaration of the function.
Even your is_specialization falls short, as it assumes that all template arguments are type arguments. It wouldn't work for std::array, since one of its arguments is a value, not a type.
C++ has no convenient mechanism to say what you're trying to say. You have to spell it out, or accept some less-than-ideal compromise.
Also, broadly speaking, it's probably not a good idea. If your function already must be a template, what would be the harm in taking any sized_range? Once you start expanding templates like this, you're going to find yourself less likely to be bound to specific types and more willing to accept any type that fulfills a particular concept.
That is, it's rare to have a function that is specific enough that it needs vector, but general enough that it doesn't have requirements on the value_type of that vector too.
Note that is not valid in standard C++20, but you can achieve exactly what you want with the following syntax that is supported by GCC as an extension.
std::vector<auto>
Which is shorthand for std::vector<T> where T is unconstrained.
http://coliru.stacked-crooked.com/a/6422d0284d299b85
How about this syntax?
int64_t signed_vector_size(const instance_of<std::vector> auto& v){
return (int64_t)v.size();
}
Basically we want to be able to say "this argument should be an instance of some template". So, say that?
template<template<class...>class Z, class T>
struct is_instance_of : std::false_type {};
template<template<class...>class Z, class...Ts>
struct is_instance_of<Z, Z<Ts...>> : std::true_type {};
template<class T, template<class...>class Z>
concept instance_of = is_instance_of<Z, T>::value;
int64_t signed_vector_size(const instance_of<std::vector> auto& v){
return (int64_t)v.size();
}
that should do it. Note that I don't make a Vec alias; you can pass in partial arguments to a concept. The type you are testing is prepended.
Live example.
Now, I'd actually say this is a bit of an anti-pattern. I mean, that size? Why shouldn't it work on non-vectors? Like, std::spans or std::deques.
Also, instance_of doesn't support std::array, as one of the arguments isn't a type. There is no way to treat type, template and value arguments uniformly in C++ at this point.
For each pattern of type, template and value arguments you'd need a different concept. Which is awkward.
Given
template <typename S, typename T>
T make_T(S const &s) { ... }
How can I leave S to be derived while explicitly providing T?
I would like to be able to say:
auto t = make_T<auto, int>(S{});
but clang and gcc tell me that auto is not allowed in template argument.
Had the arguments happened to be reversed in the prototype of make_T,
then all would be well;
I could explicitly give T and leave S to be derived.
In a previous question,
the proposed solution was to declare a helper function that reversed the arguments, e.g.,
template <typename T, typename S>
T make_T_reversed(S const &s) { return make_T<S,T>(s); }
which now enables
auto t = make_T_reversed<int>(S{});
as desired, but I'm hoping there might be a more direct way that doesn't require creating temporary helper functions. I'm asking as a new question because the accepted answer of the previous question doesn't answer my actual question:
is there a direct means of achieving this?
I'm feeling hopeful that with C++17 and C++20 (not around at the time of the previous question), there may now be, but I've sadly been unable to find it.
Further motivating examples
The use case initially motivating the question was that I wanted to write
std::unordered_set<T, default, default, Allocator> obj;
using the default values for the middle two template parameters
(Hash and KeyEqual),
but explicitly specifying the Allocator parameter.
I'm using the default constructor, so the type for Allocator cannot be derived.
I realise the question I actually asked isn't quite the same (I asked about deriving the values rather than taking the default values), but I'm hoping the same approach would work for both cases:
auto t = make_T<auto, int>(S{});
std::unordered_set<T, auto, auto, Allocator> obj;
if S is from T when T is provided, what you will provide for S?
make_T<auto, int>() is definitely impossible, but make_T<void, int>() may be acceptable for you?
template<typename nS, typename T, typename S = std::conditional_t<std::is_same_v<void, nS>, T, nS>>
T make_T(S const&);
but S is always deduced by the argument, why do you want S to be the first parameter?
or you want S to be determined?
template<typename nS, typename T>
T make_T(std::conditional_t<std::is_same_v<void, nS>, T, nS> const&);
Lets assume I have a template function
template <typename T>
void do_sth(T const&);
For some types ("small" and copyable) it would be better to pass an argument by value instead of reference.
So my question is: What is the simplest way to overload a function depending on the underlying type?
Remark:
Best what I came to is using enable_if with some conditions of "simple" type. And I believe there's no such a type trait as "simple type" in the standard library. Correct me if I'm wrong. Moreover: Using enable_if gets complicated as a function takes more template arguments (edited) because template <typename T, typename U> void do_sth(T, U) would need 4 overloads: (value, value), (value, ref), (ref, value) and (ref, ref).
Don't do this. Template functions are inlined at the drop of a hat, and a reference to an x is an alias with no identity once the function is inlined.
Encourage the function to be inlined instead of doing a mess of SFINAE, unless and until you have proven this to be an important bottleneck.
After discovering this function is taking up more time than anything else you can optimize, test its improvement by manualy writing the by value version in a couple of key cases to ensure you actually get a benefit (I doubt you will).
boost has call_traits:
template <typename T>
void do_sth(typename boost::call_traits<T>::param_type)
But one big issue is that T is no longer deducible
(and so you have to call it do_sth<int>(42) or do_sth<MyBigObj>(myBigObj)).
So might be used for non template methods in template class:
template <typename T>
struct S
{
void do_sth(typename boost::call_traits<T>::param_type);
};
But anyway, chances are that compiler actually inlines code, resulting in same generated code.
Since you can take integral values as template parameters and perform arithmetic on them, what's the motivation behind boost::mpl::int_<> and other integral constants? Does this motivation still apply in C++11?
You can take integral values as template parameters, but you cannot take both types and non-type template parameters with a single template. Long story short, treating non-type template parameters as types allows for them to be used with a myriad of things within MPL.
For instance, consider a metafunction find that works with types and looks for an equal type within a sequence. If you wished to use it with non-type template parameters you would need to reimplement new algorithms 'overloads', a find_c for which you have to manually specify the type of the integral value. Now imagine you want it to work with mixed integral types as the rest of the language does, or that you want to mix types and non-types, you get an explosion of 'overloads' that also happen to be harder to use as you have to specify the type of each non-type parameter everywhere.
This motivation does still apply in C++11.
This motivation will still apply to C++y and any other version, unless we have some new rule that allows conversion from non-type template parameters to type template parameters. For instance, whenever you use 5 and the template requests a type instantiate it with std::integral_constant< int, 5 > instead.
tldr; Encoding a value as a type allows it to be used in far more places than a simple value. You can overload on types, you can't overload on values.
K-Ballo's answer is great.
There's something else I think is relevant though. The integral constant types aren't only useful as template parameters, they can be useful as function arguments and function return types (using the C++11 types in my examples, but the same argument applies to the Boost ones that predate them):
template<typename R, typename... Args>
std::integral_constant<std::size_t, sizeof...(Args)>
arity(R (*)(Args...))
{ return {}; }
This function takes a function pointer and returns a type telling you the number of arguments the function takes. Before we had constexpr functions there was no way to call a function in a constant expression, so to ask questions like "how many arguments does this function type take?" you'd need to return a type, and extract the integer value from it.
Even with constexpr in the language (which means the function above could just return sizeof...(Args); and that integer value would be usable at compile time) there are still good uses for integral constant types, e.g. tag dispatching:
template<typename T>
void frobnicate(T&& t)
{
frob_impl(std::forward<T>(t), std::is_copy_constructible<T>{});
}
This frob_impl function can be overloaded based on the integer_constant<bool, b> type passed as its second argument:
template<typename T>
void frob_impl(T&& t, std::true_type)
{
// do something
}
template<typename T>
void frob_impl(T&& t, std::false_type)
{
// do something else
}
You could try doing something similar by making the boolean a template parameter:
frob_impl<std::is_copy_constructible<T>::value>(std::forward<T>(t));
but it's not possible to partially specialize a function template, so you couldn't make frob_impl<true, T> and frob_impl<false, T> do different things. Overloading on the type of the boolean constant allows you to easily do different things based on the value of the "is copy constructible" trait, and that is still very useful in C++11.
Another place where the constants are useful is for implementing traits using SFINAE. In C++03 the conventional approach was to have overloaded functions that return two types with different sizes (e.g an int and a struct containing two ints) and test the "value" with sizeof. In C++11 the functions can return true_type and false_type which is far more expressive, e.g. a trait that tests "does this type have a member called foo?" can make the function indicating a positive result return true_type and make the function indicating a negative result return false_type, what could be more clear than that?
As a standard library implementor I make very frequent use of true_type and false_type, because a lot of compile-time "questions" have true/false answers, but when I want to test something that can have more than two different results I will use other specializations of integral_constant.