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.
Related
I've got the following code:
template <bool condition>
struct enable_if { };
template <>
struct enable_if<true> { using type = bool; };
template <typename T>
class is_callable {
using Yes = char[1];
using No = char[2];
template <typename U> static Yes& filter(decltype(&U::operator()));
template <typename U> static No& filter(...);
public:
constexpr operator bool() { return sizeof(filter<T>(nullptr)) == sizeof(Yes); }
};
template <typename Lambda, typename enable_if<is_callable<Lambda>{}>::type = true>
void doSomethingWithLambda(Lambda func) {
func();
}
int main() {
doSomethingWithLambda([]() { });
}
The important part is the enable_if<is_callable<Lambda>{}>::type part.
One is forced to instantiate is_callable<Lambda> with {} because if one were to use (), C++ would mistake it for a function call.
Feel free to correct me if I'm wrong, but as far as I know, C++ assumes it is a function in the () case so that the type of expression isn't determined after the time of writing, saving everyone a headache. What I mean by that is, assuming you had a function version and a class version of is_callable (separated by SFINAE using enable_if or something along those lines), the type Lambda could determine the true meaning of (), either a function call or an instantiation. Like I said, as far as I know, C++ wants to avoid this confusion, so it assumes function call and fails if such a function does not exist.
Based on the assumptions above, the following shouldn't work:
enable_if<(bool)is_callable<Lambda>()>::type
What does it matter if I cast the result of the function call (never mind that functions couldn't even be evaluated in this context)? Why is this suddenly treated as an instantiation instead of a function call?
No, your understanding is not correct.
Firstly, a name can't refer to both a class template and a function template. If that happens the program is ill-formed. (And defining both in the same scope is not allowed to begin with.)
Secondly, is_callable<Lambda>() as template argument is not a function call to begin with. It is a function type. It is the type of a function which has no parameters and returns a is_callable<Lambda>.
When the compiler parses a template argument, it can interpret it in two ways: Either as a type or as an expression (or as a braced-init-list), because template parameters can be type parameters or non-type parameters.
When the compiler reads is_callable<Lambda>() it notices that is_callable is a class template and then realizes that is_callable<Lambda> is therefore a type. If you have a type, let's shorten it to T, then T() can either be syntax representing the type of a function returning T and taking no arguments, or it can be an expression formed from one single functional notation explicit cast (which you imprecisely call "instantiation").
There is no way to differentiate these two cases in the context, but the compiler needs to know whether this is a type template argument or a non-type template argument. So there is a rule saying that such ambiguities are always resolved in favor of a type.
If is_callable was a function template instead, there would be no ambiguity, because then is_callable<Lambda> is not a type and therefore is_callable<Lambda>() cannot be a function type. It must be a function call instead and therefore an expression and non-type template argument.
When you write (bool)is_callable<Lambda>() this is not valid syntax for a type and therefore there is no ambiguity. It is a non-type template argument and an expression. And is_callable<Lambda>() is a funcational notation explicit cast because is_callable<Lambbda> is a type. If is_callable was a function template instead of a class template, then it would be a function call.
I just started on C++ so sorry if this is a newbie-ish question. I searched all over the web and didn't find anything about this. In fact I wasn't even sure how to formulate my search...
I saw this code somewhere:
template <class T>
struct SomeStruct
{
SomeStruct() {}
};
And later, this:
int main()
{
SomeStruct<void (Foo::*)(int test)> mStruct;
}
The above compiles just fine.
So if I understood it correctly, "void (Foo::*)(int test)" is a function pointer that points to some function in Foo taking a int as argument and returning void.
How can that be a legal argument for the "class T" parameter?
Any help would be appreciated.
void (Foo::*)(int test) is a type of pointer to member function. A variable of such type can be used to point to member function of class Foo (that returns void and takes a single int argument).
class T is a misnomer there - arbitrary type can be used as a template parameter (the type doesn't have to be declared as class), regardless if the template is declared with template<class T> or template<typename T>.
For this reason I don't use the first form, only the latter.
In context of template parameter list of template declaration, typename and class can be used interchangeably, except you must use class in template template parameters (like template<template<typename, typename> class> before C++1z.
It's a function pointer type. Perfectly legitimate.
The argument doesn't have to be an actual class; the use of class there is misleading, and is equivalent to the clearer typename.
If you want to really be freaked out, look up non-type template arguments, because you can in fact pass an actual function pointer (with a little modification). :)
I was reading book "template compelte guide" , I section : 12.2.1 Signatures I couldn't understand a sentence of author :
Its return type, if the function is generated from a function template
what does the author means by "function generated by function template" ? , is he talking about template-id here? if so, why does return type matters in the case, since signature is defined by us?
A trivial example will be helpful, thanks.
In section 12.2.1 the authors describe under which situations declarations of functions can coexist. The declaration of function templates can coexist even if they have a different return type, e.g., you can declare:
template <typename T> int f();
tepmlate <typename T> char f();
If you make these functions non-templates, you can't even declare them. Of course, in the form above you won't be able to call the function although you may be able to explicitly the select one of the functions using a cast (I'm not sure about this).
The primary use of having functions with different return types coexist (although this wasn't the original intent), is to remove some of these functions from the overload set based on condition, e.g.:
template <typename T>
typename std::enable_if<std::numeric_limits<T>::is_specialized, T>::type
f(T); // used for types for which std::numeric_limits<T> is specialied
template <typename T>
typename std::enable_if<!std::numeric_limits<T>::is_specialized, T>::type
f(T); // used for types for which std::numeric_limits<T> is not specialied
OK, suppose I want to check whether the template parameter has a nested type/typedef XYZ.
template <class T>
struct hasXZY
{
typedef char no;
typedef struct { char x[2]; } yes;
template <class U>
static yes f(typename U::XYZ*);
template <class /*U*/>
static no f(...);
enum {value = sizeof(f<T>(0))==sizeof(yes)};
};
Works fine, as expected.
Now consider this:
template <class T>
struct hasXZY
{
typedef char no;
typedef struct { char x[2]; } yes;
static yes f(typename T::XYZ*);
static no f(...);
enum {value = sizeof(f(0))==sizeof(yes)};
};
hasXYZ<int> now results in a compile-time error. OK, f is not a template function. But on the other hand when hasXYZis instantiated for int via hasXYZ<int>::value, the compiler could easily just exclude f(int::XYZ*) from candidate list. I just don't understand why a failure in the instantiation of a member functions declaration in a class template must make the whole class instantiation fail. Any ideas?
Edit: My question is: why should the member function declararions be all well-formed? Since the compiler instantiates the methods only upon their usage, why does it need correct declaration. Consider the above example2 as a possible use-case of this feature.
SFINAE is used only when creating a candidate set for a function overload resolution. In your first example, you are calling the overloaded f() function, and the first one is excluded thanks to SFINAE.
In your second example, when instantiate hasXZY, all its members must be well defined, and the substitution of the template parameter must not fail. It does for int::XYZ.
Members will not be excluded from the class because of a substitution failure.
I'm not a C++ language lawyer, but I'll have a go at this.
In your second example, the member functions must be well-defined because they are no longer template functions once hasXZY is instantiated for int. To convince yourself of this, do the substitution for T "by hand":
struct hasXYZ
{
typedef int T;
typedef char no;
typedef struct { char x[2]; } yes;
static yes f(T::XYZ*);
static no f(...);
enum {value = sizeof(f(0))==sizeof(yes)};
};
int main()
{
std::cout << hasXYZ::value << "\n";
}
and observe that this fails to compile, with the same compiler error as before (in GCC, at least):
foo.cc:9: error: âTâ is not a class or namespace
By contrast, the first example compiles and behaves as expected after manual instantiation; the f members are still templated on U.
Edit: My question is: why should the member function declararions be all well-formed? Since the compiler instantiates the methods only upon their usage, why does it need correct declaration. Consider the above example2 as a possible use-case of this feature.
When implicitly instantiating a class template specialization, the compiler has to inspect the complete declarator of that member because it needs to know basic information about the declaration. Such can contribute to the size of the class template specialization.
If inspecting the declaration part will find out it's declaring a data-member, the sizeof value of the class will possibly yield a different value. If you would have declared a function pointer instead, this would be the case
yes (*f)(typename T::XYZ*);
The C++ language is defined in such a way that the type of a declaration is known only once the whole declaration is parsed.
You can argue that you put static there, and thus in this case this is not needed to compute its size. But it is needed for name-lookup to know what a name hasXZY<T>::f refers to and that there was declared a name f at all. The compiler will not instantiate the definition of hasXYZ::f, but it will only instantiate the non-definition part of the declaration, to gets its type and adding its name to the class type for name lookup purposes. I believe supporting delayed-instantiation for declaration of names in particular cases where it would possibly work would complicate implementation of C++ compilers and the C++ spec even more, for no comparable benefit.
And finally, in your example where you attempt to call it, the compiler has to instantiate the declaration, because it needs to lookup the name f, and for this it needs to know whether that declaration is a function or something else. So I really even theoretically can't see a way your example could work without instantiating the declaration. Note that in any case, these will not instantiate a definition of the function.
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 :-)