Substitution failure is not an error

Last updated

Substitution failure is not an error (SFINAE) is a principle in C++ where an invalid substitution of template parameters is not in itself an error. David Vandevoorde first introduced the acronym SFINAE to describe related programming techniques. [1]

Contents

Specifically, when creating a candidate set for overload resolution, some (or all) candidates of that set may be the result of instantiated templates with (potentially deduced) template arguments substituted for the corresponding template parameters. If an error occurs during the substitution of a set of arguments for any given template, the compiler removes the potential overload from the candidate set instead of stopping with a compilation error, provided the substitution error is one the C++ standard grants such treatment. [2] If one or more candidates remain and overload resolution succeeds, the invocation is well-formed.

Example

The following example illustrates a basic instance of SFINAE:

structTest{typedefintfoo;};template<typenameT>voidf(typenameT::foo){}// Definition #1template<typenameT>voidf(T){}// Definition #2intmain(){f<Test>(10);// Call #1.f<int>(10);// Call #2. Without error (even though there is no int::foo)// thanks to SFINAE.return0;}

Here, attempting to use a non-class type in a qualified name (T::foo) results in a deduction failure for f<int> because int has no nested type named foo, but the program is well-formed because a valid function remains in the set of candidate functions.

Although SFINAE was initially introduced to avoid creating ill-formed programs when unrelated template declarations were visible (e.g., through the inclusion of a header file), many developers later found the behavior useful for compile-time introspection. Specifically, it allows a template to determine certain properties of its template arguments at instantiation time.

For example, SFINAE can be used to determine if a type contains a certain typedef:

#include<iostream>template<typenameT>structhas_typedef_foobar{// Types "yes" and "no" are guaranteed to have different sizes,// specifically sizeof(yes) == 1 and sizeof(no) == 2.typedefcharyes[1];typedefcharno[2];template<typenameC>staticyes&test(typenameC::foobar*);template<typename>staticno&test(...);// If the "sizeof" of the result of calling test<T>(nullptr) is equal to// sizeof(yes), the first overload worked and T has a nested type named// foobar.staticconstboolvalue=sizeof(test<T>(nullptr))==sizeof(yes);};structfoo{typedeffloatfoobar;};intmain(){std::cout<<std::boolalpha;std::cout<<has_typedef_foobar<int>::value<<std::endl;// Prints falsestd::cout<<has_typedef_foobar<foo>::value<<std::endl;// Prints truereturn0;}

When T has the nested type foobar defined, the instantiation of the first test works and the null pointer constant is successfully passed. (And the resulting type of the expression is yes.) If it does not work, the only available function is the second test, and the resulting type of the expression is no. An ellipsis is used not only because it will accept any argument, but also because its conversion rank is lowest, so a call to the first function will be preferred if it is possible; this removes ambiguity.

C++11 simplification

In C++11, the above code could be simplified to:

#include<iostream>#include<type_traits>template<typename...Ts>usingvoid_t=void;template<typenameT,typename=void>structhas_typedef_foobar:std::false_type{};template<typenameT>structhas_typedef_foobar<T,void_t<typenameT::foobar>>:std::true_type{};structfoo{usingfoobar=float;};intmain(){std::cout<<std::boolalpha;std::cout<<has_typedef_foobar<int>::value<<std::endl;std::cout<<has_typedef_foobar<foo>::value<<std::endl;return0;}

With the standardisation of the detection idiom in the Library fundamental v2 (n4562) proposal, the above code could be re-written as follows:

#include<iostream>#include<type_traits>template<typenameT>usinghas_typedef_foobar_t=typenameT::foobar;structfoo{usingfoobar=float;};intmain(){std::cout<<std::boolalpha;std::cout<<std::is_detected<has_typedef_foobar_t,int>::value<<std::endl;std::cout<<std::is_detected<has_typedef_foobar_t,foo>::value<<std::endl;return0;}

The developers of Boost used SFINAE in boost::enable_if [3] and in other ways.

Related Research Articles

Templates are a feature of the C++ programming language that allows functions and classes to operate with generic types. This allows a function or class to work on many different data types without being rewritten for each one.

Template metaprogramming (TMP) is a metaprogramming technique in which templates are used by a compiler to generate temporary source code, which is merged by the compiler with the rest of the source code and then compiled. The output of these templates can include compile-time constants, data structures, and complete functions. The use of templates can be thought of as compile-time polymorphism. The technique is used by a number of languages, the best-known being C++, but also Curl, D, Nim, and XL.

In computer programming, a function object is a construct allowing an object to be invoked or called as if it were an ordinary function, usually with the same syntax. Function objects are often called functors.

A function pointer, also called a subroutine pointer or procedure pointer, is a pointer that points to a function. As opposed to referencing a data value, a function pointer points to executable code within memory. Dereferencing the function pointer yields the referenced function, which can be invoked and passed arguments just as in a normal function call. Such an invocation is also known as an "indirect" call, because the function is being invoked indirectly through a variable instead of directly through a fixed identifier or address.

Partial template specialization is a particular form of class template specialization. Usually used in reference to the C++ programming language, it allows the programmer to specialize only some arguments of a class template, as opposed to explicit full specialization, where all the template arguments are provided.

In the C++ programming language, a reference is a simple reference datatype that is less powerful but safer than the pointer type inherited from C. The name C++ reference may cause confusion, as in computer science a reference is a general concept datatype, with pointers and C++ references being specific reference datatype implementations. The definition of a reference in C++ is such that it does not need to exist. It can be implemented as a new name for an existing object.

In the C++ programming language, argument-dependent lookup (ADL), or argument-dependent name lookup, applies to the lookup of an unqualified function name depending on the types of the arguments given to the function call. This behavior is also known as Koenig lookup, as it is often attributed to Andrew Koenig, though he is not its inventor.

In object-oriented programming, a destructor is a method which is invoked mechanically just before the memory of the object is released. It can happen when its lifetime is bound to scope and the execution leaves the scope, when it is embedded in another object whose lifetime ends, or when it was allocated dynamically and is released explicitly. Its main purpose is to free the resources which were acquired by the object during its life and/or deregister from other entities which may keep references to it. Use of destructors is needed for the process of Resource Acquisition Is Initialization (RAII).

typedef is a reserved keyword in the programming languages C, C++, and Objective-C. It is used to create an additional name (alias) for another data type, but does not create a new type, except in the obscure case of a qualified typedef of an array type where the typedef qualifiers are transferred to the array element type. As such, it is often used to simplify the syntax of declaring complex data structures consisting of struct and union types, although it is also commonly used to provide specific descriptive type names for integer data types of varying sizes.

A class in C++ is a user-defined type or data structure declared with keyword class that has data and functions as its members whose access is governed by the three access specifiers private, protected or public. By default access to members of a C++ class is private. The private members are not accessible outside the class; they can be accessed only through methods of the class. The public members form an interface to the class and are accessible outside the class.

sizeof is a unary operator in the programming languages C and C++. It generates the storage size of an expression or a data type, measured in the number of char-sized units. Consequently, the construct sizeof (char) is guaranteed to be 1. The actual number of bits of type char is specified by the preprocessor macro CHAR_BIT, defined in the standard include file limits.h. On most modern computing platforms this is eight bits. The result of sizeof has an unsigned integer type that is usually denoted by size_t.

The C and C++ programming languages are closely related but have many significant differences. C++ began as a fork of an early, pre-standardized C, and was designed to be mostly source-and-link compatible with C compilers of the time. Due to this, development tools for the two languages are often integrated into a single product, with the programmer able to specify C or C++ as their source language.

The curiously recurring template pattern (CRTP) is an idiom, originally in C++, in which a class X derives from a class template instantiation using X itself as a template argument. More generally it is known as F-bound polymorphism, and it is a form of F-bounded quantification.

C++11 is a version of the ISO/IEC 14882 standard for the C++ programming language. C++11 replaced the prior version of the C++ standard, called C++03, and was later replaced by C++14. The name follows the tradition of naming language versions by the publication year of the specification, though it was formerly named C++0x because it was expected to be published before 2010.

In computing, compile-time function execution is the ability of a compiler, that would normally compile a function to machine code and execute it at run time, to execute the function at compile time. This is possible if the arguments to the function are known at compile time, and the function does not make any reference to or attempt to modify any global state.

In computer programming, variadic templates are templates that take a variable number of arguments.

In C++ computer programming, allocators are a component of the C++ Standard Library. The standard library provides several data structures, such as list and set, commonly referred to as containers. A common trait among these containers is their ability to change size during the execution of the program. To achieve this, some form of dynamic memory allocation is usually required. Allocators handle all the requests for allocation and deallocation of memory for a given container. The C++ Standard Library provides general-purpose allocators that are used by default, however, custom allocators may also be supplied by the programmer.

"typename" is a keyword in the C++ programming language used when writing templates. It is used for specifying that a dependent name in a template definition or declaration is a type. In the original C++ compilers before the first ISO standard was completed, the typename keyword was not part of the C++ language and Bjarne Stroustrup used the class keyword for template arguments instead. While typename is now the preferred keyword, older source code may still use the class keyword instead.

In the context of the programming language C++, functional refers to a header file that is part of the C++ Standard Library and provides a set of predefined class templates for function objects, including operations for arithmetic, comparisons, and logic. Instances of these class templates are C++ classes that define a function call operator, and the instances of these classes can be called as if they were functions. It is possible to perform very sophisticated operations without writing a new function object, simply by combining predefined function objects and function object adaptors.

In the C++ programming language, input/output library refers to a family of class templates and supporting functions in the C++ Standard Library that implement stream-based input/output capabilities. It is an object-oriented alternative to C's FILE-based streams from the C standard library.

References

  1. Vandevoorde, David; Nicolai M. Josuttis (2002). C++ Templates: The Complete Guide. Addison-Wesley Professional. ISBN   0-201-73484-2.
  2. International Organization for Standardization. "ISO/IEC 14882:2003, Programming languages C++", § 14.8.2.
  3. Boost Enable If