What is a non-deduced context?

I’ve stumbled over Why is the template argument deduction not working here? recently and the answers can be summed up to “It’s a non-deduced context”.

Specifically, the first one says it’s such a thing and then redirects to the standard for “details”, while the second one quotes the standard, which is cryptic to say the least.

Can someone please explain to mere mortals, like myself, what a non-deduced context is, when does it occur, and why does it occur?

1

Deduction refers to the process of determining the type of a template parameter from a given argument. It applies to function templates, auto, and a few other cases (e.g. partial specialization). For example, consider:

template <typename T> void f(std::vector<T>);

Now if you say f(x), where you declared std::vector<int> x;, then T is deduced as int, and you get the specialization f<int>.

In order for deduction to work, the template parameter type that is to be deduced has to appear in a deducible context. In this example, the function parameter of f is such a deducible context. That is, an argument in the function call expression allows us to determine what the template parameter T should be in order for the call expression to be valid.

However, there are also non-deduced contexts, where no deduction is possible. The canonical example is “a template parameter that appears to the left of a :::

template <typename> struct Foo;

template <typename T> void g(typename Foo<T>::type);

In this function template, the T in the function parameter list is in a non-deduced context. Thus you cannot say g(x) and deduce T. The reason for this is that there is no “backwards correspondence” between arbitrary types and members Foo<T>::type. For example, you could have specializations:

 template <> struct Foo<int>       { using type = double; };
 template <> struct Foo<char>      { using type = double; };
 template <> struct Foo<float>     { using type = bool; };
 template <> struct Foo<long>      { int type = 10; };
 template <> struct Foo<unsigned>  { };

If you call g(double{}) there are two possible answers for T, and if you call g(int{}) there is no answer. In general, there is no relationship between class template parameters and class members, so you cannot perform any sensible argument deduction.


Occasionally it is useful to inhibit argument deduction explicitly. This is for example the case for std::forward. Another example is when you have conversions from Foo<U> to Foo<T>, say, or other conversions (think std::string and char const *). Now suppose you have a free function:

template <typename T> bool binary_function(Foo<T> lhs, Foo<T> rhs);

If you call binary_function(t, u), then the deduction may be ambiguous and thus fail. But it is reasonable to deduce only one argument and not deduce the other, thus permitting implicit conversions. Now an explicitly non-deduced context is needed, for example like this:

template <typename T>
struct type_identity {
    using type = T;
};

template <typename T>
bool binary_function(Foo<T> lhs, typename type_identity<Foo<T>>::type rhs)
{
    return binary_function(lhs, rhs);
}

(You may have experienced such deduction problems with something like std::min(1U, 2L).)

Note: std::type_identity is available in the standard library since C++20.

9

What is a non-deduced context?

A non-deduced context is a context where template arguments cannot be deduced from some construct.
For example:

template <int N>
void foo(int x = N * N); // called like foo()

It’s obviously impossible to deduce the argument for the non-type template parameter N from the default argument N * N. Not only would the compiler have to take the square root of N * N (despite the language having no such “square root deduction”), it would also have to come up with a value for N * N out of thin air.

List of non-deduced contexts c++20

In the following list, assume that

  • T is some template type parameter
  • N is some non-type template parameter
template <typename T, int N>

Each heading cites and explains one bullet in [temp.deduct.type] p5.

1. Foo<T>::type

The nested-name-specifier of a type that was specified using a qualified-id.

There is no way to deduce T because type is just an alias such as std::vector<T>::size_type. Knowing only the size_type (which is std::size_t in this case), how could we possibly figure out T? We cannot, because the information isn’t contained within std::size_t.

The most common way that developers run into this problem is by trying to deduce the container from an iterator.

template <typename T>
void foo(typename T::iterator it); // attempt to deduce T from an iterator

int main() {
    std::array<int> v;
    foo(v.begin()); // error, trying to deduce std::array<int> from int* (probably)
}                   // which is obviously imposssible

2. decltype(N)

The expression of a decltype-specifier.

template <int N>
void foo(decltype(N * N - N) n);

The expression of decltype can be arbitrarily complex, and a decltype specifier is a type. If N is a non-type template parameter, how could we possibly know the value from just the type (e.g. guess 123 from int)?

3. Foo<0 + N>, int(&)[0 + N]

A non-type template argument or an array bound in which a subexpression references a template parameter.

It would be possible to deduce N from just Foo<N> or from an array bound, but when N only appears as a subexpression, it becomes generally impossible. It is theoretically possible for a simple expression such as 0 + N, but this quickly gets out of hand for more complex expressions such as N * N.

4. void foo(T x = T{})

A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.

If we call such a function like foo(), then deduction would require some circular logic. The type T would be inferred from the default argument T{}, whose type is T, which is inferred from the default argument, …


Note: this case applies to the motivating example at the start of the answer.

5. void foo(T* function_pointer) with overload sets

A function parameter for which the associated argument is an overload set ([over.over]), and one or more of the following apply:

  • more than one function matches the function parameter type (resulting in an ambiguous deduction), or
  • no function matches the function parameter type, or
  • the overload set supplied as an argument contains one or more function templates.

This may not be so obvious, so I’ll provide an example:

void take(int);
void take(float);

template <typename T>
void foo(T* function_pointer) { function_pointer(0); }
// note: T(*function_pointer)(int)  would work, and T would deduce to void

int main() { foo(&take); } // error

Both take(int) and take(float) match the function parameter, so it’s ambiguous whether T should deduce to void(int) or void(float).

6. std::initializer_list function arguments

A function parameter for which the associated argument is an initializer list ([dcl.init.list]) but the parameter does not have a type for which deduction from an initializer list is specified ([temp.deduct.call]).

The example in the C++ standard demonstrates this well:

template<class T> void g(T);
g({1,2,3});                 // error: no argument deduced for T

Of all the bullets, this restriction is the most artificial. {1, 2, 3} could by considered std::initializer_list<int>, but it was intentionally decided not to make this deduction.

7. void foo(Ts..., int)

A function parameter pack that does not occur at the end of the parameter-declaration-list.

The template parameter pack Ts... cannot be deduced.
The basic issue is that if we called foo(0) this is ambiguous between providing 0 as an argument to the pack, or to the int parameter.
In function templates, this ambiguity is resolved by interpreting the parameter pack as an empty pack, which resolves some cases, but not all:

template <typename... Ts>
void foo(Ts..., int);

int main() {
    foo(0);     // OK, Ts... is an empty pack
    foo(0, 0);  // ill-formed despite being intuitively unambiguous
}

Further notes

There numerous rules that need to be followed for deduction to be possible.
Not deducing from a non-deduced context is just one of them.
[temp.deduct.type] p8 lists the forms that a type must have for deduction to be possible.

Another indirect rule related to arrays is this:

template <int N> foo(int(&)[N]); // N can be deduced from array size
template <int N> foo(int[N]);    // N cannot be deduced because arrays in parameters
                                 // are adjusted to pointers

Intentionally disabling type deduction

Sometimes developers intentionally disable deduction because they want the user of a function template to provide an argument explicitly.
This can be done with std::type_identity in C++20, or with a user-defined version of it prior to C++20.

template <typename T>
void foo(std::type_identity<T>::type); // non-deduced context in function parameter

4

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật