std::in_place_type and std::in_place_index in C++
In modern C++, std::in_place_type and
std::in_place_index serve as disambiguation tags that
enable direct, in-place construction of objects inside container-like
wrappers such as std::variant, std::optional,
and std::any. Introduced in C++17, these utility tags allow
developers to pass constructor arguments directly to the contained
object instead of creating and copying or moving a temporary instance.
This article explores how these tags work, why they are essential for
performance and non-movable types, and how they resolve constructor
ambiguities in variant types.
The Purpose of In-Place Construction
When initializing a wrapper like std::optional or
std::variant, passing a pre-constructed object requires
that object to be either copied or moved into the wrapper's internal
storage:
std::vector<int> data = {1, 2, 3};
std::optional<std::vector<int>> opt(data); // Incurs a copyTo eliminate this overhead, C++ provides in-place construction.
Passing an in-place tag informs the wrapper's constructor that the
remaining arguments should be forwarded directly to the constructor of
the contained type. This avoids temporaries, improves efficiency, and
allows the initialization of non-copyable and non-movable types (such as
std::mutex).
Using
std::in_place_type
std::in_place_type is a tag template that specifies the
exact type to construct inside the target wrapper. It is commonly used
with std::variant to specify which alternative to
instantiate, particularly when the type requires multiple constructor
arguments.
#include <variant>
#include <string>
#include <vector>
int main() {
// Directly constructs std::string with 5 occurrences of 'a'
std::variant<int, std::string, std::vector<int>> v(
std::in_place_type<std::string>, 5, 'a'
);
}Without std::in_place_type, creating this variant would
require constructing a temporary std::string(5, 'a') and
moving it into the variant.
Although std::optional typically uses the simpler
std::in_place tag because it holds only one type,
std::in_place_type<T> is also accepted.
Using
std::in_place_index
std::in_place_index is a tag template that identifies
the alternative in a std::variant by its zero-based
position rather than its type.
This tag is essential in scenarios where
std::in_place_type cannot be used:
- Duplicate Types: A
std::variantcan contain multiple alternatives of the same type. In this case, specifying a type is ambiguous. - Ambiguous Conversions: When several alternatives can accept the provided constructor arguments, specifying the index removes any compiler ambiguity.
#include <variant>
#include <string>
int main() {
// Variant containing duplicate types
using DuplicateVariant = std::variant<std::string, std::string>;
// Constructs the second std::string (index 1) directly in place
DuplicateVariant v(std::in_place_index<1>, "Second Alternative");
}Summary of Benefits
- Performance: Arguments are forwarded directly to the target constructor, eliminating unnecessary copy and move operations.
- Support for Immovable Types: Enables containers
like
std::optionalandstd::variantto hold types that disable both copy and move constructors. - Disambiguation:
std::in_place_typeclearly states the intended type for multi-argument constructors, whilestd::in_place_indexsolves ambiguity for variants with repeated or convertible types.