---
title: "Makestd::make_from_tupleSFINAE friendly"
document: P3738R1
date: 2026-05-06
audience: Library Evolution Working Group
reply-to:
  - "Yihan Wang <yronglin777@gmail.com>"
  - "Wenming Wang <wenmingw@nvidia.com>"
  - "Jun Yang <juney@nvidia.com>"
paper-type: proposal
---

# Changelog

## R1

- Make the original Mandates as Constraints.

## R0

Initial revision

# Introduction

This paper introduce constraints for `std::make_from_tuple` to make it SFINAE friendly.

# Motivation

[[LWG3528]](https://wg21.link/lwg3528) introduce constraints:

```cpp
template<class T, class Tuple, size_t... I>
  requires is_constructible_v<T, decltype(get<I>(declval<Tuple>()))...>
constexpr T make-from-tuple-impl(Tuple&& t, index_sequence<I...>) {     // exposition only
  return T(get<I>(std::forward<Tuple>(t))...);
}
```

When someone write SFINAE code like the following to check whether `T` can constructed from a tuple, they may hit hard errors like “no matching function for call to `make-from-tuple-impl` ” ([Compiler Explorer](https://godbolt.org/z/qs9aon1oM)).

```cpp
template <typename T, typename Tuple, typename = void>
inline constexpr bool has_make_from_tuple = false;

template <typename T, typename Tuple>
inline constexpr bool has_make_from_tuple<
    T, Tuple,
    std::void_t<decltype(std::make_from_tuple<T>(std::declval<Tuple>()))>> =
    true;
struct A {
    int a;
};
static_assert(!has_make_from_tuple<int *, std::tuple<A *>>);
```

Even If the effects are *Equivalent to* calling a constrained function, the constraints has not apply to `std::make_from_tuple` .

This is somehow unclear when the constraints are not literally specified with *Constraints* in the standard wording (16.3.2.4 [[structure.specifications]](https://wg21.link/structure.specifications)). At least *Equivalent to* doesn’t propagate every substitution failure in immediate context. In the case of `make-from-tuple-impl` , the constraints were introduced via a requires-clause but not literal *Constraints*. Some implementors believed the requires-clause should be treated same as *Constraints*, but this is not explicitly stated.

This proposal also suggests changing the *Mandates* “If `tuple_size_v<remove_reference_t<Tuple>>` is 1, then `reference_constructs_from_temporary_v<T, decltype(get<0>(declval<Tuple>()))>` is `false` ” to a *Constraints*; this would yield similar benefits for the following example:

```cpp
static_assert(!has_make_from_tuple<const int &, std::tuple<short>>);
```

# Impact on the Standard

This proposal is a pure library improvement for the current specification [[N4944]](https://wg21.link/n4944).

# Implementation Experience

I’ve partial implemented this improvement in [`libc++`](https://github.com/llvm/llvm-project/pull/85263), [`microsoft/STL`](https://github.com/microsoft/STL/pull/4528), [`libstdc++`](https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=73edc003c0a8f0badc7027e6deefd3a573300b03).

The constraints “If `tuple_size_v<remove_reference_t<Tuple>>` is 1, then `reference_constructs_from_temporary_v<T, decltype(get<0>(declval<Tuple>()))>` is `false` ” was not implemented in these libraries.

# Proposed Wording

Modify section 22.4.6 22.4.6 [[tuple.apply]](https://wg21.link/tuple.apply) as indicated:

```cpp
template<class T, tuple-like Tuple>
 constexpr T make_from_tuple(Tuple&& t);
```

2 *Mandates*: If `tuple_size_v<remove_reference_t<Tuple>>` is 1, then `reference_constructs_from_temporary_v<T, decltype(get<0>(declval<Tuple>()))>` is `false` .

2 Let `I` be the pack `0, 1, ..., (tuple_size_v<remove_reference_t<Tuple>> - 1)` .

3 *Constraints*:

- `is_constructible_v<T, decltype(get<I>(declval<Tuple>()))...>` is `true` .
- If `tuple_size_v<remove_reference_t<Tuple>>` is 1, then `reference_constructs_from_temporary_v<T, decltype(get<0>(declval<Tuple>()))>` is `false` .

*Effects*: Given the exposition-only function template:

```cpp
namespace std {
  template<class T, tuple-like Tuple, size_t... I>
    requires is_constructible_v<T, decltype(get<I>(declval<Tuple>()))...>
    requires is_constructible_v<T, decltype(get<I>(declval<Tuple>()))...> &&
             tuple_size_v<std::remove_reference_t<_Tuple>> == 1 &&
             !reference_constructs_from_temporary_v<T, decltype(get<0>(declval<Tuple>()))>
  constexpr T make-from-tuple-impl(Tuple&& t, index_sequence<I...>) {   // exposition only
    return T(get<I>(std::forward<Tuple>(t))...);
  }
}
```

*Equivalent to*:

```cpp
return make-from-tuple-impl<T>(
           std::forward<Tuple>(t),
           make_index_sequence<tuple_size_v<remove_reference_t<Tuple>>>{});
```

[ *Note:* The type of T must be supplied as an explicit template parameter, as it cannot be deduced from the argument list. — *end note* ]

# Acknowledgements

Thank you to Mark Hoemmen, Jiang An, Mark de Wever, Stephan T. Lavavej, Jonathan Wakely, Barry Revzin, Daniel Krügler, and everyone else who contributed to the discussions, and encouraged me to write this paper.

# References

[LWG3528] Tim Song. make_from_tuple can perform (the equivalent of) a
C-style cast.

https://wg21.link/lwg3528

[N4944] Thomas Köppe. 2023-03-22. Working Draft, Standard for
Programming Language C++.

https://wg21.link/n4944
