std::declval


Keyword: 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.

Example 1: Basic Usage in Type Traits

#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;
}

Explanation

Example 2: Using 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;
}

Explanation

Example 3: Return Type Deduction in Unevaluated Contexts

#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;
}

Explanation

Additional Considerations

  1. std::declval is only allowed in unevaluated contexts. Attempting to use it in an evaluated context will result in a compilation error.

  2. It's particularly useful when working with types that are not default-constructible or when you want to avoid invoking constructors.

  3. std::declval is often used in combination with decltype and other type traits to perform compile-time type analysis and metaprogramming.

Summary

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:

  1. Deducing return types of functions or methods.
  2. Implementing type traits that need to "inspect" properties of types.
  3. Working with types that are not default-constructible.
  4. SFINAE (Substitution Failure Is Not An Error) techniques in template metaprogramming.

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.

Related

Previous Page | Course Schedule | Course Content