I saw code like the following:
using EnableIfIntegral = typename std::enable_if<
std::is_integral<T>::value || std::is_enum<T>::value, int>::type;
template <typename T, EnableIfIntegral<T> = 0>
constexpr int Seconds(T n) {
return time_internal::FromInt64(n, std::ratio<1>{});
}
template <std::intmax_t N>
constexpr int FromInt64(int64_t v, std::ratio<1, N>) {
// Do Something
}
I understand what a template function is. Why in the template parameter list, does it have SomeClass<T> = 0? I know T is the template parameter.
Why is std::ratio<1, N> a parameter?
I'll answer the first of your two questions.
using EnableIfIntegral = typename std::enable_if<
std::is_integral<T>::value || std::is_enum<T>::value, int>::type;
I'm fairly certain that you accidentally left out the previous line, which
should be
template<typename T>
So the full declaration is
template<typename T>
using EnableIfIntegral = typename std::enable_if<
std::is_integral<T>::value || std::is_enum<T>::value, int>::type;
This is pretty big pill to swallow, and I'll come back and discuss the finer details of this declaration in a moment, but what happens here is that if the stars and the moons are alined just right, this becomes:
template<typename T>
using EnableIfIntegral = int;
In other words, a template type that's just a simple alias for an garden variety int. Nothing more than that. Moving onto to the next declaration:
template <typename T, EnableIfIntegral<T> = 0>
constexpr int Seconds(T n) {
This simply becomes
template <typename T, int = 0>
constexpr int Seconds(T n) {
In other words, a simple template parameter, an int constant that defaults to 0.
That's it, and nothing more. This does absolutely nothing, and that's precisely the intended result here.
The first template parameter, T gets deduced from the actual parameter to the Seconds() template function. Then the 2nd template parameter passes the T template type to the EnableIfIntegral template alias declaration, which, hopefully, if all the stars and the moons are properly aligned, does nothing.
Now, when do the stars and the moons properly align? It's when the deduced template type is either some integral type or some enum type. Then everything just works.
But if you do something silly, like:
std::string what_is_this;
Seconds(what_is_this);
A very loose description of what will happen: the first template parameter to Seconds gets deduced as std::string. So far so good, but we're already doomed because what's inside the Seconds template function will not be able to handle a std::string value. Normally this will typically result in a typical, indecipherable (to mere mortals) C++ compiler error message.
But things are not going to get this far. What happens first is that the deduced T template parameter, which is now std::string, gets forwarded to EnableIfIntegral, and this template alias will fail to resolve. For all the gory details why, look up what std::enable_if does in your favorite C++ book. The exact details of how that happens is not very important, the point is that the template substitution for the Seconds() template itself will fail. The compiler will not find a suitable Seconds template that will match this function call.
This situation typically produces a much simpler error message from your compiler. Your compiler's error message will be much basic, something along the lines "Hey, what's this Seconds template being called here? I know nothing about it". You will look at the code and realize "Silly me, I'm passing a std::string but I need to pass an integral value instead". Oops.
Overall, what this is, is just a common approach to make the pain of using a C++ library less painful, when things go wrong and don't compile. Without this trick, the compiler will whine about the time_internal::FromInt64 function call (based on the shown code in the question), but there's nothing wrong with that function call. The problem is really with whatever calls Seconds, that's where the problem is, and this common approach helps the compiler produce a better error message.
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;
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&);
So I'm designing a sort of my_numeric_cast function to limit the types of conversions available when using a framework I'm writing.
It was pretty straight forward to do something like
template<typename To, typename From>
constexpr To my_numeric_cast(From);
template<>
constexpr float my_numeric_cast<float, int>(int i) { return i; }
Which works, allowing only casting from ints to floats whenever the cast is used. And producing a linkage error whenever a cast not in the white list is attempted.
However, I'd really want to make this a compilation error, to catch the misuse much faster.
How do I make the base template body valid, expect when instantiating it?
You cannot write a template function specialization for which no template argument makes the body valid in C++. The result if you do so is an ill formed program with no diagnostic required. This includes the primary specialization.
So most of the answers here are simply undefined behaviour. They may work, but they are not valid C++. They may work today, but after a library upgrade a compiler upgrade or a different build target they could fail in completely different and surprising ways. Relying on UB with no strong reason is a bad idea.
On the plus side, we can do away with template specialization and fix your problem in one fell swoop:
template<class T>struct tag_t{}; // may need `constexpr tag_t(){}` on some compilers
template<class T>constexpr tag_t<T> tag{};
template<class T, class F>
constexpr T my_numeric_cast(F, tag_t<F>)=delete; // generates compile time error
constexpr float my_numeric_cast(int i, tag_t<float>) { return i; } // not a template! Could be if you want it to be.
template<typename To, typename From>
constexpr To my_numeric_cast(From f){
return my_numeric_cast(f, tag<To>);
}
and done.
=delete generates friendly messages. Program is well formed. Implementing casts is no longer a specialization. You can even implement it in the namespace of a type being cast to or from as ADL is enabled.
If you solve a problem with template function specialization, reconsider. They are fragile, do not work like class template specialization or function overloading (while looking like both of them!), and usually are not the best solution to anything. There are exceptions when it may be a good idea, but they are quite rare, and given how rare they are avoiding the quirky feature may still be worth it.
You can use traits to get a compile time error:
template<typename, typename> struct RetTraits;
// Enable it for int -> float
template<> struct RetTraits<int, float> { using type = float; };
template<typename To, typename From>
using RetType = typename RetTraits<To, From>::type;
template<typename To, typename From>
constexpr RetType<To, From> my_numeric_cast(From f) {
return To(f);
}
int main() {
my_numeric_cast<int, float>(42);
// This won't compile
// my_numeric_cast<int, int>(42);
}
I noted that much of boost and libc++/libstdc++ explicitly provide a default value of zero for SFINAE in code like
// libc++ http://llvm.org/svn/llvm-project/libcxx/trunk/include/memory
namespace __has_pointer_type_imp
{
template <class _Up> static __two __test(...);
template <class _Up> static char __test(typename _Up::pointer* = 0);
}
template <class _Tp>
struct __has_pointer_type
: public integral_constant<bool, sizeof(__has_pointer_type_imp::__test<_Tp>(0)) == 1>
{
};
However it confuses me as to why this would be expected when they explicitly make the call with 0. I remember hearing somewhere it was an optimization (to speed up the compiler when instantiating the template) but I don't fully understand how that would work. I looked at the standard and it has a section that briefly describes what happens with default-arguments in relation to template argument deduction.
14.8.2
At certain points in the template argument deduction process it is necessary to take a function type that makes use of template parameters and replace those template parameters with the corresponding template arguments. This is done at the beginning of template argument deduction when any explicitly specified template arguments are substituted into the function type, and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.
The last bit there sounds concerning to my question
and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.
However that sounds like the opposite of an optimization if it has to do more work. Does anyone have any reasoning why that 0 must be there, it works without it, but every single example of SFINAE in libc++ at least seems to explicitly put 0 there, even though they never call the function with no arguments.
You give a default value because you want to be able to not give that parameter, though the condition is still check !
Example:
template<typename T,
typename std::enable_if<
std::is_floating_point_v<T>
>::type = 0>
void foo() { std::cout << "1"; }
One can then call this function in the main with only one template parameter! Better, one does not even need to name the second parameter since it is unused.
A similar example when the parameter is given in the input of the function foo:
template<class T>
void foo(T t,
typename std::enable_if<
std::is_floating_point_v<T>
>::type = 0) { std::cout << "2"; }
then you could give a second parameter to the function foobut you do not want to do that in general.
I'm having a problem with an enable_if construct which I've managed to reduce to a very simple piece of failing code:
template <typename Enable, typename...Args>
struct Get;
template <typename FirstArg, typename... OtherArgs>
struct Get<typename std::enable_if<true>::type, FirstArg, OtherArgs...>
{
using type = FirstArg;
};
(Live example)
The above code is a gutted version of some code that did something useful, but for the sake of this question I'm more interested in why this doesn't work, not whether or not it's ideal or does anything useful. The meta function Get should take the first type passed to it and, based on some condition (in this case always true as I didn't construct a case for when it is not) return it.
I don't want the first argument to resolve to the enable_if condition, that should be completely abstracted.
When I try to run this (see live example) it produces
error: ‘type’ in ‘struct Get’ does not name a type using T =
typename Get::type
Why?
The specialization you wrote only kicks in if someone passed void as the first argument to Get.
In your example code, you passed int. So the specialization does not apply.
The names of the types in the template definition and specialization have no connection, just their position in the Get<blah, blah, blah>. And it is pattern matching off the primary definition.
int main() {
using T = Get<void, int>::type;
}