SFINAE + sizeof = detect if expression compiles - c++

I just found out how to check if operator<< is provided for a type.
template<class T> T& lvalue_of_type();
template<class T> T rvalue_of_type();
template<class T>
struct is_printable
{
template<class U> static char test(char(*)[sizeof(
lvalue_of_type<std::ostream>() << rvalue_of_type<U>()
)]);
template<class U> static long test(...);
enum { value = 1 == sizeof test<T>(0) };
typedef boost::integral_constant<bool, value> type;
};
Is this trick well-known, or have I just won the metaprogramming Nobel prize? ;)
EDIT: I made the code simpler to understand and easier to adapt with two global function template declarations lvalue_of_type and rvalue_of_type.

It's a well known technique, I'm afraid :-)
The use of a function call in the sizeof operator instructs the compiler to perform argument deduction and function matching, at compile-time, of course. Also, with a template function, the compiler also instantiates a concrete function from a template. However, this expression does does not cause a function call to be generated. It's well described in SFINAE Sono Buoni PDF.
Check other C++ SFINAE examples.

It is just a combination of two well-known tricks. SFINAE says 'substitution failure is not an error' - that's exactly what you did. Using sizeof to let the compiler substitute template arguments into an expression without actually executing it is also common.
Sorry :-)

Related

Check existence of operator()

I need a type trait HasCall to check validity of the folowing type instantiation for T:
template<class T> struct Caller: T
{
using T::operator();
};
Is there a way to do this in C++14?
Here is my attempt, but it doesn't work:
https://godbolt.org/z/vxgJCR
EDIT
I know about SFINAE and how it works. This problem is more dificult than just checking validity of some expression.
I want these asserts to pass:
struct A {void operator()(int) {}};
static_assert(HasCall<A>, "Check must work with any set of arguments.");
struct B {void operator()() {}};
static_assert(HasCall<B>, "Check must work with any set of arguments.");
struct C {template<typename... Args> void operator()(Args&&...) {}};
static_assert(HasCall<C>, "Templated operators must be detected correctly.");
struct D {};
static_assert(!HasCall<D>, "No operator() at all.");
static_assert(!HasCall<void(*)()>, "Class cannot inherit from function pointers.");
Checking validity of expression &T::operator() is not enough because it doesn't work with overloaded or template operator().
Please, check your solutions with these asserts.
This question is not a duplicate.
No, there is no way to do this.
All solutions require knowing the signature, either exact or compatible, of the call, or rely on no overloads.
In fact any overloaded or template call operator cannot be reliably detected even if you have a known signature, as implicit cast to function pointer permits declval tests to be spoofed.
You will have to find another way around your problem, or wait for reflection.
Try this:
template <typename T, typename = void> struct has_operator {
enum { value = 0 };
};
// thanks super for great suggestion!
template <typename T> struct has_operator<T, std::void_t<decltype(std::declval<T>()())>> {
enum { value = 1 };
};
template<class T, typename = std::enable_if<has_operator<T>::value>> struct Caller: T
{
using T::operator();
};
This works by principle of SFINAE - some errors in template instantation instead of failing whole compilation will just make compiler ignore given instantation. So first we define has_operator with value = 0, which is our "default value". Then we make specialization of the template for type T. Now we want this specialization to be picked only, when T::operator() exists. So we add second template argument and set it's default value to decltype(&T::operator()) and value = 1. We don't care for real type here, what we care is that if T::operator() exists, this will compile just fine. And compiler will select this specialization for this T. When it doesn't exist - compiler will ignore this specialization and select "default" has_operator, which has value = 0.
So now we have struct has_operator, which - when used like this: has_operator<T>::value will produce constant value 0, when T don't have operator () (with any arguments, mind you) and value 1, when has. You can use it with std::enable_if (which btw works in pretty much the same way).
The list of things, that can be applied with this technique is rather long - pretty much anything, that can make or break compilation can be used.

Way to pass argument to template function

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.

How is class function without definition not causing error

I was learning some C++ idioms when I came across a sample code for SFINAE, I was perplexed with the code.
Note: is_ptr() has no definition.
I compiled the code myself, no compiler errors regarding lack of function definition, why?
sizeof() was used on one of the calls, it apparently executed on the returns of the functions, but then again, there was no definition. How was that possible?
template <typename T>
struct is_pointer
{
template <typename U>
static char is_ptr(U*);
template <typename X, typename Y>
static char is_ptr(X Y::*);
template <typename U>
static char is_ptr(U (*)());
static double is_ptr(...);
static T t;
enum {value = sizeof(is_ptr(t)) == sizeof(char)};
};
sizeof is what is called an un-evaluated context. Within it the expression needs to be only well formed, nothing will actually ever be executed. So a function that is only declared can be used inside without needing an actual definition.
This is because the definition is not required to determine the type information. In this case, the overload that is chosen by ADL is the type information in question. It's return type is also available from the declaration alone.

Making `std::get` play nice with SFINAE

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.

Should the following code compile according to C++ standard?

#include <type_traits>
template <typename T>
struct C;
template<typename T1, typename T2>
using first = T1;
template <typename T>
struct C<first<T, std::enable_if_t<std::is_same<T, int>::value>>>
{
};
int main ()
{
}
Results of compilation by different compilers:
MSVC:
error C2753: 'C': partial specialization cannot match argument list for primary template
gcc-4.9:
error: partial specialization 'C' does not specialize any template arguments
clang all versions:
error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list
gcc-5+:
successfully compiles
And additionaly I want to point out that trivial specialization like:
template<typename T>
struct C<T>
{
};
successfully fails to be compiled by gcc. So it seems like it figures out that specialization in my original example is non-trivial. So my question is - is pattern like this explicitly forbidden by C++ standard or not?
The crucial paragraph is [temp.class.spec]/(8.2), which requires the partial specialization to be more specialized than the primary template. What Clang actually complains about is the argument list being identical to the primary template's: this has been removed from [temp.class.spec]/(8.3) by issue 2033 (which stated that the requirement was redundant) fairly recently, so hasn't been implemented in Clang yet. However, it apparently has been implemented in GCC, given that it accepts your snippet; it even compiles the following, perhaps for the same reason it compiles your code (it also only works from version 5 onwards):
template <typename T>
void f( C<T> ) {}
template <typename T>
void f( C<first<T, std::enable_if_t<std::is_same<T, int>::value>>> ) {}
I.e. it acknowledges that the declarations are distinct, so must have implemented some resolution of issue 1980. It does not find that the second overload is more specialized (see the Wandbox link), however, which is inconsistent, because it should've diagnosed your code according to the aforementioned constraint in (8.2).
Arguably, the current wording makes your example's partial ordering work as desired†: [temp.deduct.type]/1 mentions that in deduction from types,
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values […] that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
Now via [temp.alias]/3, this would mean that during the partial ordering step in which the partial specialization's function template is the parameter template, the substitution into is_same yields false (since common library implementations just use a partial specialization that must fail), and enable_if fails.‡ But this semantics is not satisfying in the general case, because we could construct a condition that generally succeeds, so a unique synthesized type meets it, and deduction succeeds both ways.
Presumably, the simplest and most robust solution is to ignore discarded arguments during partial ordering (making your example ill-formed). One can also orientate oneself towards implementations' behaviors in this case (analogous to issue 1157):
template <typename...> struct C {};
template <typename T>
void f( C<T, int> ) = delete;
template <typename T>
void f( C<T, std::enable_if_t<sizeof(T) == sizeof(int), int>> ) {}
int main() {f<int>({});}
Both Clang and GCC diagnose this as calling the deleted function, i.e. agree that the first overload is more specialized than the other. The critical property of #2 seems to be that the second template argument is dependent yet T appears solely in non-deduced contexts (if we change int to T in #1, nothing changes). So we could use the existence of discarded (and dependent?) template arguments as tie-breakers: this way we don't have to reason about the nature of synthesized values, which is the status quo, and also get reasonable behavior in your case, which would be well-formed.
† #T.C. mentioned that the templates generated through [temp.class.order] would currently be interpreted as one multiply declared entity—again, see issue 1980. That's not directly relevant to the standardese in this case, because the wording never mentions that these function templates are declared, let alone in the same program; it just specifies them and then falls back to the procedure for function templates.
‡ It isn't entirely clear with what depth implementations are required to perform this analysis. Issue 1157 demonstrates what level of detail is required to "correctly" determine whether a template's domain is a proper subset of the other's. It's neither practical nor reasonable to implement partial ordering to be this sophisticated. However, the footnoted section just goes to show that this topic isn't necessarily underspecified, but defective.
I think you could simplify your code - this has nothing to do with type_traits. You'll get the same results with following one:
template <typename T>
struct C;
template<typename T>
using first = T;
template <typename T>
struct C<first<T>> // OK only in 5.1
{
};
int main ()
{
}
Check in online compiler (compiles under 5.1 but not with 5.2 or 4.9 so it's probably a bug) - https://godbolt.org/g/iVCbdm
I think that int GCC 5 they moved around template functionality and it's even possible to create two specializations of the same type. It will compile until you try to use it.
template <typename T>
struct C;
template<typename T1, typename T2>
using first = T1;
template<typename T1, typename T2>
using second = T2;
template <typename T>
struct C<first<T, T>> // OK on 5.1+
{
};
template <typename T>
struct C<second<T, T>> // OK on 5.1+
{
};
int main ()
{
C<first<int, int>> dummy; // error: ambiguous template instantiation for 'struct C<int>'
}
https://godbolt.org/g/6oNGDP
It might be somehow related to added support for C++14 variable templates. https://isocpp.org/files/papers/N3651.pdf