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);
}
Related
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.
Assume that we want to make a template class that can only be instantiated with numbers and should not compile otherwise. My attempt:
#include <type_traits>
template<typename T, typename = void>
struct OnlyNumbers{
public:
struct C{};
static_assert(std::is_same<C,T>::value, "T is not arithmetic type.");
//OnlyNumbers<C>* ptr;
};
template<typename T>
struct OnlyNumbers<T, std::enable_if_t<std::is_arithmetic_v<T>>>{};
struct Foo{};
int main()
{
OnlyNumbers<int>{}; //Compiles
//OnlyNumbers<Foo>{}; //Error
}
Live demo - All three major compilers seem to work as expected. I'm aware that there is already a similar question with answers quoting the standard. The accepted answer uses temp.res.8 together with temp.dep.1 to answer that question. I think my question is a bit different because I'm asking precisely about my example and I'm not sure about the standard's opinion on it.
I would argue that my program is not ill-formed and that it should fail to compile if and only if the compiler tries to instantiate the base template.
My reasoning:
[temp.dep.1]:
Inside a template, some constructs have semantics which may differ from one instantiation to another. Such a construct depends on the template parameters.
This should make std::is_same<C,T>::value dependent on T.
[temp.res.8.1]:
no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or
Does not apply because there exist a valid specialization, in particular OnlyNumbers<C> is valid and can be used inside the class for e.g. defining a member pointer variable(ptr). Indeed by removing the assert and uncommenting the ptr, OnlyNumbers<Foo> lines the code compiles.
[temp.res.8.2 - 8.4] does not apply.
[temp.res.8.5] I don't think this applies either but I cannot say that I fully understand this section.
My question is: Is my reasoning correct? Is this a safe, standard-compliant way to make a particular [class]* template fail to compile using static_assert if** and only if it is instantiated?
*Primarily I'm interested in class templates, feel free to include function templates. But I think the rules are the same.
**That means that there is no T which can be used to instantiate the template from the outside like T=C could be used from the inside. Even if C could be accessed somehow I don't think there's a way to refer to it because it leads to this recursion OnlyNumbers<OnlyNumbers<...>::C>.
EDIT:
Just to clarify, I know that I can construct an expression that will be false exactly if none of the other specializations match. But that can become wordy quite quickly and is error-prone if specializations change.
Static assertions are there to be used directly in the class without doing anything complicated.
#include <type_traits>
template<typename T>
struct OnlyNumbers {
static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
// ....
};
In some cases, you might get additional error messages since instanciating OnlyNumbers for non-arithmetic types might cause more compilation errors.
One trick I have used from time to time is
#include <type_traits>
template<typename T>
struct OnlyNumbers {
static_assert(std::is_arithmetic_v<T>, "T is not arithmetic type.");
using TT = std::conditional_t<std::is_arithmetic_v<T>,T,int>;
// ....
};
In this case, your class gets instanciated with int, a valid type. Since the static assertion fails anyway, this does not have negative effects.
Well... I don't understand what do you mean with
[[temp.res.8.1]] Does not apply because there exist a valid specialization, in particular OnlyNumbers is valid and can be used inside the class for e.g. defining a member pointer variable(ptr).
Can you give an example of OnlyNumers valid and compiling main template based on OnlyNumbers<C>?
Anyway, it seems to me that the point is exactly this.
If you ask
Is this a safe, standard-compliant way to make a particular [class]* template fail to compile using static_assert if** and only if it is instantiated?
it seems to me that (maybe excluding a test that is true only when another specialization matches) the answer is "no" because of [temp.res.8.1].
Maybe you could let a little open door open to permit an instantiation but available only is someone really (really!) want instantiate it.
By example, you could add a third template parameter, with different default value, and something as follows
template<typename T, typename U = void, typename V = int>
struct OnlyNumbers
{
static_assert(std::is_same<T, U>::value, "test 1");
static_assert(std::is_same<T, V>::value, "test 2");
};
This way you open a door to a legit instantiation
OnlyNumbers<Foo, Foo, Foo> o1;
OnlyNumbers<void, void, void> o2;
OnlyNumbers<int, int> o3;
but only explicating at least a second template type.
Anyway, why don't you simply avoid to define the main version of the template?
// declared but (main version) not defined
template<typename T, typename = void>
struct OnlyNumbers;
// only specialization defined
template<typename T>
struct OnlyNumbers<T, std::enable_if_t<std::is_arithmetic_v<T>>>
{ };
Your code is ill-formed since the primary template cannot be instantiated. See the standard quote in Barry's answer to the related question you linked to. The roundabout way you have used to ensure that the clearly stated standard requirement cannot be met, does not help. Stop fighting your compiler rsp. the standard, and go with Handy999's approach. If you still don't want to do that e.g. for DRY reasons, then a conformant way to achieve your goal would be:
template<typename T, typename Dummy = void>
struct OnlyNumbers{
public:
struct C{};
static_assert(! std::is_same<Dummy, void>::value, "T is not a number type.");
Two remarks:
First, I deliberately replaced the error message because the error message "is not an arithmetic type" screams that you must test ! std::is_arithmetic<T>::value. The approach I've outlined potentially makes sense if you have multiple overloads for "numeric" types, some of which meet the standard's arithmetic type requirements and others might not (e.g. maybe a type from a multiprecision library).
Second, you might object that someone could write e.g. OnlyNumbers<std::string, int> to defeat the static assertion. To which I say, that's their problem. Remember that every time you make something idiot proof, nature makes a better idiot. ;-) Seriously, do make APIs that are easy to use and hard to abuse, but you cannot fix insanity and shouldn't bother trying.
TL;DR: KISS and SWYM (say what you mean)
std::get does not seem to be SFINAE-friendly, as shown by the following test case:
template <class T, class C>
auto foo(C &c) -> decltype(std::get<T>(c)) {
return std::get<T>(c);
}
template <class>
void foo(...) { }
int main() {
std::tuple<int> tuple{42};
foo<int>(tuple); // Works fine
foo<double>(tuple); // Crashes and burns
}
See it live on Coliru
The goal is to divert the second call to foo towards the second overload. In practice, libstdc++ gives:
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/6.3.0/../../../../include/c++/6.3.0/tuple:1290:14: fatal error: no matching function for call to '__get_helper2'
{ return std::__get_helper2<_Tp>(__t); }
^~~~~~~~~~~~~~~~~~~~~~~
libc++ is more direct, with a straight static_assert detonation:
/usr/include/c++/v1/tuple:801:5: fatal error: static_assert failed "type not found in type list"
static_assert ( value != -1, "type not found in type list" );
^ ~~~~~~~~~~~
I would really like not to implement onion layers checking whether C is an std::tuple specialization, and looking for T inside its parameters...
Is there a reason for std::get not to be SFINAE-friendly? Is there a better workaround than what is outlined above?
I've found something about std::tuple_element, but not std::get.
std::get<T> is explicitly not SFINAE-friendly, as per [tuple.elem]:
template <class T, class... Types>
constexpr T& get(tuple<Types...>& t) noexcept;
// and the other like overloads
Requires: The type T occurs exactly once in Types.... Otherwise, the program is ill-formed.
std::get<I> is also explicitly not SFINAE-friendly.
As far as the other questions:
Is there a reason for std::get not to be SFINAE-friendly?
Don't know. Typically, this isn't a point that needs to be SFINAE-ed on. So I guess it wasn't considered something that needed to be done. Hard errors are a lot easier to understand than scrolling through a bunch of non-viable candidate options. If you believe there to be compelling reason for std::get<T> to be SFINAE-friendly, you could submit an LWG issue about it.
Is there a better workaround than what is outlined above?
Sure. You could write your own SFINAE-friendly verison of get (please note, it uses C++17 fold expression):
template <class T, class... Types,
std::enable_if_t<(std::is_same<T, Types>::value + ...) == 1, int> = 0>
constexpr T& my_get(tuple<Types...>& t) noexcept {
return std::get<T>(t);
}
And then do with that as you wish.
Don't SFINAE on std::get; that is not permitted.
Here are two relatively sfinae friendly ways to test if you can using std::get; get<X>(t):
template<class T,std::size_t I>
using can_get=std::integral_constant<bool, I<std::tuple_size<T>::value>;
namespace helper{
template<class T, class Tuple>
struct can_get_type:std::false_type{};
template<class T, class...Ts>
struct can_get_type<T,std::tuple<Ts...>>:
std::integral_constant<bool, (std::is_same_v<T,Ts>+...)==1>
{};
}
template<class T,class Tuple>
using can_get=typename helpers::can_get_type<T,Tuple>::type;
Then your code reads:
template <class T, class C, std::enable_if_t<can_get_type<C,T>{},int> =0>
decltype(auto) foo(C &c) {
return std::get<T>(c);
}
From N4527 (I presume it's still in the standard):
§ 20.4.2.6 (8):
Requires: The type T occurs exactly once in Types.... Otherwise, the program is ill-formed.
The program above is ill-formed, according to the standard.
End of discussion.
Almost no function in STL is SFINAE-friendly, this is the default.
Maybe it is purely historical. (as in "C++ has all the defaults wrong").
But perhaps a post-facto justification could be that SFINAE-friendlyness has a cost (e.g. compile time).
I don't have proof, but I think it is fair to say that SF-code takes longer to compile because it has to "keep trying" when rejecting alternatives instead of bailing out on the first error.
As #Barry said it has also a mental cost because SFINAE is harder to reason about than hard errors.
(At least before one has the "concepts" clear.)
If the user wants SFINAE it can be built (with a lot of effort) on top of non-SFINAE friendly with help of traits.
For example, one can always write (#Barry wrote the equivalent for std::get)
template<class In, class Out, class=std::enable_if_t<std::is_assignable<std::iterator_traits<Out>::reference, std::iterator_traits<In>::reference> >
Out friendly_copy(In first, In last, Out d_last){
return std::copy(first, last, d_first);
}
Honestly, I find myself wrapping many STL functions this way, but it is a lot of work to get it right.
So, I guess that there is a place for a SFINAE-friendly version of STL.
In some sense this is comming if requires are added to the signatures of the current functions.
I don't know if this is the plan exactly but it might be a side effect of introducing concepts to the language.
I hope so.
I am wishful here, but I want to be precise about the question. I want to improve implementation of detecting member templates by their names alone, but I came across an obstacle. I can't find any way to detect overloaded static/non-static member functions. Very important part of this question is, that I can't use this trick, because it detects any name (I'm already using it in current implementation, kinda useful), and I can't specify the types, because that misses the point of using names alone.
For reference, see my other question. I posted current has_member_template_bar implementation as an answer there. I don't think I should post all of it in this question.
The question stays the same - Can we detect overloaded member functions, without specifying their arguments, or using mentioned trick? I know I'm maybe asking for impossible, but it never hurts to ask.
The question stays the same - Can we detect overloaded member functions, without specifying their arguments, or using mentioned trick? I know I'm maybe asking for impossible, but it never hurts to ask
Actually, it is not impossible.
It follows a minimal, working example:
template<typename T, typename R, typename... A>
void proto(R(T::*)(A...));
template<typename, typename = void>
constexpr bool has_single_f = false;
template<typename T>
constexpr bool has_single_f<T, decltype(proto(&T::f))> = true;
struct S {
void f(int) {}
void f(char) {}
};
struct U {
void f() {}
};
int main() {
static_assert(not has_single_f<S>, "!");
static_assert(has_single_f<U>, "!");
}
Of course, you need to add more stuff to distinguish between member methods and data members, but it's trivial to add them (see the header type_traits) and I tried to keep the example as minimal as possible.
The basic idea is that the call to proto fails if the given function is overloaded, for it's an ambiguous call.
Anyway, the error is discarded because of SFINAE rules.
Arguments are not required to be specified as requested.
Note also that proto does not require to be defined, but it's important that its return type is void. Otherwise you have to slightly modify the call to decltype as it follows:
template<typename T>
constexpr bool has_single_f<T, decltype(proto(&T::f), void())> = true;
As you can see from the example code, the static_asserts verify that f is overloaded in S and it is not in U.
The example above is based on template variables, that are part of the language since C++14.
If you prefer the well known struct based solution that works with C++11, you can use the following detector:
#include<type_traits>
//...
template<typename, typename = void>
struct has_single_f: std::false_type {};
template<typename T>
struct has_single_f<T, decltype(proto(&T::f))>: std::true_type {};
//...
int main() {
static_assert(not has_single_f<S>::value, "!");
static_assert(has_single_f<U>::value, "!");
}
Let’s say I have a templated function:
template<typename... Args>
void my_log_function(Args&&... args)
{
// Work, work, work.
}
And then invoke it as:
my_log_function("1");
my_log_function("22");
my_log_function("333");
my_log_function("4444");
my_log_function("55555");
Since the type of a string literal is const char[n], I believe this will create a lot of instantiations of my_log_function (Visual Studio Intellisense seems to suggest this). Has anyone observed this as a problem? Can it be? Can it be prevented?
Yes, this should create a different instance of class template. They should only differ in the size of the array passed by reference and if they array isn't really used any further as array, the generated code should be identical. As far as I know, VC++ folds identical code. If the type is propagated a lot, it may yield code bloat, however.
One way to prevent the code bloat is to conditionally decay types and forward to another template doing the actual work. I think this should do the trick:
template <typename... Args>
void my_real_log_function(Args&&... args) {
// real implementation goes here
}
template <typename T>
struct decay_array {
typedef typename std::condition<
std::is_array<typename std::remove_reference<T>::type>::value,
typename std::decay<T>::type,
T>::type type;
};
template <typename... Args>
void my_log_function(Args&&... args) {
my_real_log_function(std::forward<typename std::decay_array<Args>::type>(args)...);
}
The original version just using typename std::decay<T>::type doesn't work because it would turn references to non-arrays into values which isn't intended.
The solution is not to implement "work, work, work" in such a function. Sanitize the variable arguments into something more canonical and hand the work to a non-template.
Mind the bloat and remember that the template system is designed to generate inline functions.