std::declval
std::declval
is a utility function template introduced in C++11. It's primarily used in template metaprogramming and type traits to "declare" a value of a given type, without actually creating an object. This is particularly useful when working with types that are not default-constructible or when you need to deduce the return type of a function in unevaluated contexts.
#include <iostream>
#include <type_traits>
#include <utility>
class NonDefaultConstructible {
public:
NonDefaultConstructible(int) {}
int getValue() const { return 42; }
};
int main() {
using T = decltype(std::declval<NonDefaultConstructible>().getValue());
std::cout << "The return type of getValue() is "
<< (std::is_same<T, int>::value ? "int" : "not int") << std::endl;
return 0;
}
NonDefaultConstructible
that cannot be default-constructed.std::declval<NonDefaultConstructible>()
to "create" a value of this type without actually constructing it.decltype(std::declval<NonDefaultConstructible>().getValue())
deduces the return type of getValue()
.std::is_same
to check if the deduced type is int
.std::declval
in SFINAE#include <iostream>
#include <type_traits>
#include <utility>
template<typename T, typename = void>
struct has_toString : std::false_type {};
template<typename T>
struct has_toString<T,
std::void_t<decltype(std::declval<T>().toString())>> : std::true_type {};
struct WithToString {
std::string toString() const { return "Hello"; }
};
struct WithoutToString {
void someOtherMethod() {}
};
int main() {
std::cout << "WithToString has toString(): "
<< has_toString<WithToString>::value << std::endl;
std::cout << "WithoutToString has toString(): "
<< has_toString<WithoutToString>::value << std::endl;
return 0;
}
has_toString
to check if a type has a toString()
method.std::declval<T>()
is used to "create" an instance of T
without actually constructing it.decltype(std::declval<T>().toString())
attempts to call toString()
on the "declared" value.toString()
exists, the specialization with std::void_t
is selected, resulting in true_type
.toString()
doesn't exist, SFINAE causes the primary template (false_type) to be selected.#include <iostream>
#include <type_traits>
#include <utility>
template<typename T, typename U>
auto add(T t, U u) -> decltype(std::declval<T>() + std::declval<U>()) {
return t + u;
}
int main() {
auto result1 = add(5, 3.14);
auto result2 = add(std::string("Hello, "), "World!");
std::cout << "Result 1: " << result1 << std::endl;
std::cout << "Result 2: " << result2 << std::endl;
std::cout << "Type of result1: "
<< (std::is_same<decltype(result1), double>::value ? "double" : "not double")
<< std::endl;
std::cout << "Type of result2: "
<< (std::is_same<decltype(result2), std::string>::value ? "std::string" : "not std::string")
<< std::endl;
return 0;
}
add
that can work with any two types that support the +
operator.decltype(std::declval<T>() + std::declval<U>())
.std::declval<T>()
and std::declval<U>()
are used to "create" values of types T
and U
without actually constructing them.+
operation in an unevaluated context.std::declval
is only allowed in unevaluated contexts. Attempting to use it in an evaluated context will result in a compilation error.
It's particularly useful when working with types that are not default-constructible or when you want to avoid invoking constructors.
std::declval
is often used in combination with decltype
and other type traits to perform compile-time type analysis and metaprogramming.
std::declval
is a powerful utility in C++ template metaprogramming. It allows you to work with types in unevaluated contexts without actually creating objects of those types. This is especially useful for:
By using std::declval
, you can write more flexible and powerful template code, enabling better type deduction and trait implementations. However, it's important to remember that std::declval
should only be used in unevaluated contexts, typically within decltype
expressions or other compile-time constructs.