Why can't the template parameters of this function be inferred for this nested templated struct?
I have this nested templated struct:
template <typename... Ts>
struct A
template <unsigned i>
struct B
int b;
;
;
That compiles fine.
Then I try to define this function.
template <unsigned i, typename... Ts>
void foo(A<Ts...>::B<i> var)
std::cout << var.b << std::endl;
For some reason I don't completely understand, that won't compile. I had to change it in the following way for it to work.
template <unsigned i, typename... Ts>
void foo(typename A<Ts...>::template B<i> var)
std::cout << var.b << std::endl;
But then, when I call it like so:
A<int, float>::B<0> var = 0;
foo(var);
It says that template parameter i
cannot be deducted.
I can make it work by adding the template arguments explicitly to the function call:
A<int, float>::B<0> var = 0;
foo<0, int, float>(var);
Why is that? How can I make it so I don't have to specify the template parameters in the function call?
Try the code: https://repl.it/repls/LavenderDefiantOctagons
c++ templates
add a comment |
I have this nested templated struct:
template <typename... Ts>
struct A
template <unsigned i>
struct B
int b;
;
;
That compiles fine.
Then I try to define this function.
template <unsigned i, typename... Ts>
void foo(A<Ts...>::B<i> var)
std::cout << var.b << std::endl;
For some reason I don't completely understand, that won't compile. I had to change it in the following way for it to work.
template <unsigned i, typename... Ts>
void foo(typename A<Ts...>::template B<i> var)
std::cout << var.b << std::endl;
But then, when I call it like so:
A<int, float>::B<0> var = 0;
foo(var);
It says that template parameter i
cannot be deducted.
I can make it work by adding the template arguments explicitly to the function call:
A<int, float>::B<0> var = 0;
foo<0, int, float>(var);
Why is that? How can I make it so I don't have to specify the template parameters in the function call?
Try the code: https://repl.it/repls/LavenderDefiantOctagons
c++ templates
add a comment |
I have this nested templated struct:
template <typename... Ts>
struct A
template <unsigned i>
struct B
int b;
;
;
That compiles fine.
Then I try to define this function.
template <unsigned i, typename... Ts>
void foo(A<Ts...>::B<i> var)
std::cout << var.b << std::endl;
For some reason I don't completely understand, that won't compile. I had to change it in the following way for it to work.
template <unsigned i, typename... Ts>
void foo(typename A<Ts...>::template B<i> var)
std::cout << var.b << std::endl;
But then, when I call it like so:
A<int, float>::B<0> var = 0;
foo(var);
It says that template parameter i
cannot be deducted.
I can make it work by adding the template arguments explicitly to the function call:
A<int, float>::B<0> var = 0;
foo<0, int, float>(var);
Why is that? How can I make it so I don't have to specify the template parameters in the function call?
Try the code: https://repl.it/repls/LavenderDefiantOctagons
c++ templates
I have this nested templated struct:
template <typename... Ts>
struct A
template <unsigned i>
struct B
int b;
;
;
That compiles fine.
Then I try to define this function.
template <unsigned i, typename... Ts>
void foo(A<Ts...>::B<i> var)
std::cout << var.b << std::endl;
For some reason I don't completely understand, that won't compile. I had to change it in the following way for it to work.
template <unsigned i, typename... Ts>
void foo(typename A<Ts...>::template B<i> var)
std::cout << var.b << std::endl;
But then, when I call it like so:
A<int, float>::B<0> var = 0;
foo(var);
It says that template parameter i
cannot be deducted.
I can make it work by adding the template arguments explicitly to the function call:
A<int, float>::B<0> var = 0;
foo<0, int, float>(var);
Why is that? How can I make it so I don't have to specify the template parameters in the function call?
Try the code: https://repl.it/repls/LavenderDefiantOctagons
c++ templates
c++ templates
edited Nov 12 '18 at 21:45
tuket
asked Nov 12 '18 at 21:38
tukettuket
6771824
6771824
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
Because the standard says so.
The standard says so because inverting arbitrary compile time maps is equivalent to Halt.
It is equivalent to Halt because template metaprogramming is Turing complete.
It is Turing complete because it is actually hard to make a useful programming language that isn't Turing complete; it happens by accident, and avoiding it leads to a language that requires annoying workaround for seemingly simple things.
So the problem in general, of determining which A
the A<int, float>::B<0>
is from and reversing that mapping, is hard, so the C++ standard says the compiler doesn't try.
If you write your own mapping you can do it. First, you have to realize that B
types have no "hair" in that there is nothing about the type of B
that attaches it back to the type of A
used to create it. We can add "hair":
template<class...Ts>
struct A
template<unsigned i>
struct B
using daddy = A;
int b;
;
;
now we can find A
from the type of B<?>
.
Next let's add some inverse maps:
template<class...Ts>
struct types_t using type=types_t;;
template<class X>
struct get_types;
template<class X>
using get_types_t=typename get_types<X>::type;
template<template<class...>class Z, class...Ts>
struct get_types<Z<Ts...>>:types_t<Ts...>;
template<class B>
struct get_B_index;
template<unsigned i, template<unsigned>class B>
struct get_B_index<B<i>>:std::integral_constant<unsigned, i>;
and from this we can get A
's template arguments from B
:
template<class...Ts, unsigned i>
void foo( types_t<Ts...>, std::integral_constant<unsigned, i>, typename A<Ts...>::template B<i> var )
template<class B>
void foo( B b )
using types = get_types_t< typename B::daddy >;
using index = get_B_index< B >;
return foo( types, index, b );
Live example
Thanks for your answer. I wasn't expecting it to get so complicated. When you say it's equivalent to Halt are you referring to the Halting problem?
– tuket
Nov 12 '18 at 22:15
"there is nothing about the type of B that attaches it back to the type of A used to create it." Well, in general case this statement is incorrect. When you define a new nested type (a nested class definition, as is in this case), the nested type is uniquely and unambiguously attached to the enclosing type. The "no attachment" situation only appears when the nested type is an alias (e.g. a typedef name). The language spec simply does not want to distinguish between these two situations and instead opts for the "lowest common denominator".
– AnT
Nov 12 '18 at 22:52
@tuket yes, I am. It is known as Halt.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:03
@ant To be more explicit, in C++ code there is no way to go from a type A to the type it was defined in. The namespace it was defined in can be tricked out by injecting a helper function with a unique name into each namespace to test, then doing careful ADL. A way to do that could be added, but it isn't there.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:06
add a comment |
Template argument deduction can't deduce anything that appears in a nested-name-specifier. For example, it won't allow T
to be deduced from a parameter of type typename Foo<T>::bar
. I believe that the reason for this is that such deduction is not possible in general; there can always be partial specializations of Foo
that define bar
to be some arbitrarily complicated typedef.
The workaround is to define the nested type as an unnested type then bring it in using a typedef
, but use the original unnested name for deduction:
template <unsigned i, typename... Ts>
struct A_B int b;
template <typename... Ts>
struct A
template <unsigned i> using B = A_B<i, Ts...>;
;
template <unsigned i, typename... Ts>
void foo(A_B<i, Ts...> var);
1
It is simply not reasonably to deduce type in such contest. For starters, it is not necessarily one-to-one mapping.
– SergeyA
Nov 12 '18 at 21:58
Thanks, I like this solution because it's simple
– tuket
Nov 12 '18 at 22:17
add a comment |
In your specific case the nested type B
is uniquely attached to the specific specialization of the enclosing type A
. There's one-to-one correspondence between various B
s and their enclosing A
s. So, theoretically it should be possible to deduce the arguments of A
from a specific B
.
However, in general case the nested type name might refer to an alias (e.g. typedef-name), as opposed to a genuinely new type name (as in your example). In case of nested alias the 1:1 correspondence no longer holds, which is demonstrated by the examples in other answers.
The language specification does not distinguish between these two situations (a genuinely new nested type vs. a nested alias), so it opts for the "common denominator" approach and just always treats the enclosing template parameters as non-deduced context.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53270480%2fwhy-cant-the-template-parameters-of-this-function-be-inferred-for-this-nested-t%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Because the standard says so.
The standard says so because inverting arbitrary compile time maps is equivalent to Halt.
It is equivalent to Halt because template metaprogramming is Turing complete.
It is Turing complete because it is actually hard to make a useful programming language that isn't Turing complete; it happens by accident, and avoiding it leads to a language that requires annoying workaround for seemingly simple things.
So the problem in general, of determining which A
the A<int, float>::B<0>
is from and reversing that mapping, is hard, so the C++ standard says the compiler doesn't try.
If you write your own mapping you can do it. First, you have to realize that B
types have no "hair" in that there is nothing about the type of B
that attaches it back to the type of A
used to create it. We can add "hair":
template<class...Ts>
struct A
template<unsigned i>
struct B
using daddy = A;
int b;
;
;
now we can find A
from the type of B<?>
.
Next let's add some inverse maps:
template<class...Ts>
struct types_t using type=types_t;;
template<class X>
struct get_types;
template<class X>
using get_types_t=typename get_types<X>::type;
template<template<class...>class Z, class...Ts>
struct get_types<Z<Ts...>>:types_t<Ts...>;
template<class B>
struct get_B_index;
template<unsigned i, template<unsigned>class B>
struct get_B_index<B<i>>:std::integral_constant<unsigned, i>;
and from this we can get A
's template arguments from B
:
template<class...Ts, unsigned i>
void foo( types_t<Ts...>, std::integral_constant<unsigned, i>, typename A<Ts...>::template B<i> var )
template<class B>
void foo( B b )
using types = get_types_t< typename B::daddy >;
using index = get_B_index< B >;
return foo( types, index, b );
Live example
Thanks for your answer. I wasn't expecting it to get so complicated. When you say it's equivalent to Halt are you referring to the Halting problem?
– tuket
Nov 12 '18 at 22:15
"there is nothing about the type of B that attaches it back to the type of A used to create it." Well, in general case this statement is incorrect. When you define a new nested type (a nested class definition, as is in this case), the nested type is uniquely and unambiguously attached to the enclosing type. The "no attachment" situation only appears when the nested type is an alias (e.g. a typedef name). The language spec simply does not want to distinguish between these two situations and instead opts for the "lowest common denominator".
– AnT
Nov 12 '18 at 22:52
@tuket yes, I am. It is known as Halt.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:03
@ant To be more explicit, in C++ code there is no way to go from a type A to the type it was defined in. The namespace it was defined in can be tricked out by injecting a helper function with a unique name into each namespace to test, then doing careful ADL. A way to do that could be added, but it isn't there.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:06
add a comment |
Because the standard says so.
The standard says so because inverting arbitrary compile time maps is equivalent to Halt.
It is equivalent to Halt because template metaprogramming is Turing complete.
It is Turing complete because it is actually hard to make a useful programming language that isn't Turing complete; it happens by accident, and avoiding it leads to a language that requires annoying workaround for seemingly simple things.
So the problem in general, of determining which A
the A<int, float>::B<0>
is from and reversing that mapping, is hard, so the C++ standard says the compiler doesn't try.
If you write your own mapping you can do it. First, you have to realize that B
types have no "hair" in that there is nothing about the type of B
that attaches it back to the type of A
used to create it. We can add "hair":
template<class...Ts>
struct A
template<unsigned i>
struct B
using daddy = A;
int b;
;
;
now we can find A
from the type of B<?>
.
Next let's add some inverse maps:
template<class...Ts>
struct types_t using type=types_t;;
template<class X>
struct get_types;
template<class X>
using get_types_t=typename get_types<X>::type;
template<template<class...>class Z, class...Ts>
struct get_types<Z<Ts...>>:types_t<Ts...>;
template<class B>
struct get_B_index;
template<unsigned i, template<unsigned>class B>
struct get_B_index<B<i>>:std::integral_constant<unsigned, i>;
and from this we can get A
's template arguments from B
:
template<class...Ts, unsigned i>
void foo( types_t<Ts...>, std::integral_constant<unsigned, i>, typename A<Ts...>::template B<i> var )
template<class B>
void foo( B b )
using types = get_types_t< typename B::daddy >;
using index = get_B_index< B >;
return foo( types, index, b );
Live example
Thanks for your answer. I wasn't expecting it to get so complicated. When you say it's equivalent to Halt are you referring to the Halting problem?
– tuket
Nov 12 '18 at 22:15
"there is nothing about the type of B that attaches it back to the type of A used to create it." Well, in general case this statement is incorrect. When you define a new nested type (a nested class definition, as is in this case), the nested type is uniquely and unambiguously attached to the enclosing type. The "no attachment" situation only appears when the nested type is an alias (e.g. a typedef name). The language spec simply does not want to distinguish between these two situations and instead opts for the "lowest common denominator".
– AnT
Nov 12 '18 at 22:52
@tuket yes, I am. It is known as Halt.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:03
@ant To be more explicit, in C++ code there is no way to go from a type A to the type it was defined in. The namespace it was defined in can be tricked out by injecting a helper function with a unique name into each namespace to test, then doing careful ADL. A way to do that could be added, but it isn't there.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:06
add a comment |
Because the standard says so.
The standard says so because inverting arbitrary compile time maps is equivalent to Halt.
It is equivalent to Halt because template metaprogramming is Turing complete.
It is Turing complete because it is actually hard to make a useful programming language that isn't Turing complete; it happens by accident, and avoiding it leads to a language that requires annoying workaround for seemingly simple things.
So the problem in general, of determining which A
the A<int, float>::B<0>
is from and reversing that mapping, is hard, so the C++ standard says the compiler doesn't try.
If you write your own mapping you can do it. First, you have to realize that B
types have no "hair" in that there is nothing about the type of B
that attaches it back to the type of A
used to create it. We can add "hair":
template<class...Ts>
struct A
template<unsigned i>
struct B
using daddy = A;
int b;
;
;
now we can find A
from the type of B<?>
.
Next let's add some inverse maps:
template<class...Ts>
struct types_t using type=types_t;;
template<class X>
struct get_types;
template<class X>
using get_types_t=typename get_types<X>::type;
template<template<class...>class Z, class...Ts>
struct get_types<Z<Ts...>>:types_t<Ts...>;
template<class B>
struct get_B_index;
template<unsigned i, template<unsigned>class B>
struct get_B_index<B<i>>:std::integral_constant<unsigned, i>;
and from this we can get A
's template arguments from B
:
template<class...Ts, unsigned i>
void foo( types_t<Ts...>, std::integral_constant<unsigned, i>, typename A<Ts...>::template B<i> var )
template<class B>
void foo( B b )
using types = get_types_t< typename B::daddy >;
using index = get_B_index< B >;
return foo( types, index, b );
Live example
Because the standard says so.
The standard says so because inverting arbitrary compile time maps is equivalent to Halt.
It is equivalent to Halt because template metaprogramming is Turing complete.
It is Turing complete because it is actually hard to make a useful programming language that isn't Turing complete; it happens by accident, and avoiding it leads to a language that requires annoying workaround for seemingly simple things.
So the problem in general, of determining which A
the A<int, float>::B<0>
is from and reversing that mapping, is hard, so the C++ standard says the compiler doesn't try.
If you write your own mapping you can do it. First, you have to realize that B
types have no "hair" in that there is nothing about the type of B
that attaches it back to the type of A
used to create it. We can add "hair":
template<class...Ts>
struct A
template<unsigned i>
struct B
using daddy = A;
int b;
;
;
now we can find A
from the type of B<?>
.
Next let's add some inverse maps:
template<class...Ts>
struct types_t using type=types_t;;
template<class X>
struct get_types;
template<class X>
using get_types_t=typename get_types<X>::type;
template<template<class...>class Z, class...Ts>
struct get_types<Z<Ts...>>:types_t<Ts...>;
template<class B>
struct get_B_index;
template<unsigned i, template<unsigned>class B>
struct get_B_index<B<i>>:std::integral_constant<unsigned, i>;
and from this we can get A
's template arguments from B
:
template<class...Ts, unsigned i>
void foo( types_t<Ts...>, std::integral_constant<unsigned, i>, typename A<Ts...>::template B<i> var )
template<class B>
void foo( B b )
using types = get_types_t< typename B::daddy >;
using index = get_B_index< B >;
return foo( types, index, b );
Live example
answered Nov 12 '18 at 21:55
Yakk - Adam NevraumontYakk - Adam Nevraumont
184k19191376
184k19191376
Thanks for your answer. I wasn't expecting it to get so complicated. When you say it's equivalent to Halt are you referring to the Halting problem?
– tuket
Nov 12 '18 at 22:15
"there is nothing about the type of B that attaches it back to the type of A used to create it." Well, in general case this statement is incorrect. When you define a new nested type (a nested class definition, as is in this case), the nested type is uniquely and unambiguously attached to the enclosing type. The "no attachment" situation only appears when the nested type is an alias (e.g. a typedef name). The language spec simply does not want to distinguish between these two situations and instead opts for the "lowest common denominator".
– AnT
Nov 12 '18 at 22:52
@tuket yes, I am. It is known as Halt.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:03
@ant To be more explicit, in C++ code there is no way to go from a type A to the type it was defined in. The namespace it was defined in can be tricked out by injecting a helper function with a unique name into each namespace to test, then doing careful ADL. A way to do that could be added, but it isn't there.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:06
add a comment |
Thanks for your answer. I wasn't expecting it to get so complicated. When you say it's equivalent to Halt are you referring to the Halting problem?
– tuket
Nov 12 '18 at 22:15
"there is nothing about the type of B that attaches it back to the type of A used to create it." Well, in general case this statement is incorrect. When you define a new nested type (a nested class definition, as is in this case), the nested type is uniquely and unambiguously attached to the enclosing type. The "no attachment" situation only appears when the nested type is an alias (e.g. a typedef name). The language spec simply does not want to distinguish between these two situations and instead opts for the "lowest common denominator".
– AnT
Nov 12 '18 at 22:52
@tuket yes, I am. It is known as Halt.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:03
@ant To be more explicit, in C++ code there is no way to go from a type A to the type it was defined in. The namespace it was defined in can be tricked out by injecting a helper function with a unique name into each namespace to test, then doing careful ADL. A way to do that could be added, but it isn't there.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:06
Thanks for your answer. I wasn't expecting it to get so complicated. When you say it's equivalent to Halt are you referring to the Halting problem?
– tuket
Nov 12 '18 at 22:15
Thanks for your answer. I wasn't expecting it to get so complicated. When you say it's equivalent to Halt are you referring to the Halting problem?
– tuket
Nov 12 '18 at 22:15
"there is nothing about the type of B that attaches it back to the type of A used to create it." Well, in general case this statement is incorrect. When you define a new nested type (a nested class definition, as is in this case), the nested type is uniquely and unambiguously attached to the enclosing type. The "no attachment" situation only appears when the nested type is an alias (e.g. a typedef name). The language spec simply does not want to distinguish between these two situations and instead opts for the "lowest common denominator".
– AnT
Nov 12 '18 at 22:52
"there is nothing about the type of B that attaches it back to the type of A used to create it." Well, in general case this statement is incorrect. When you define a new nested type (a nested class definition, as is in this case), the nested type is uniquely and unambiguously attached to the enclosing type. The "no attachment" situation only appears when the nested type is an alias (e.g. a typedef name). The language spec simply does not want to distinguish between these two situations and instead opts for the "lowest common denominator".
– AnT
Nov 12 '18 at 22:52
@tuket yes, I am. It is known as Halt.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:03
@tuket yes, I am. It is known as Halt.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:03
@ant To be more explicit, in C++ code there is no way to go from a type A to the type it was defined in. The namespace it was defined in can be tricked out by injecting a helper function with a unique name into each namespace to test, then doing careful ADL. A way to do that could be added, but it isn't there.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:06
@ant To be more explicit, in C++ code there is no way to go from a type A to the type it was defined in. The namespace it was defined in can be tricked out by injecting a helper function with a unique name into each namespace to test, then doing careful ADL. A way to do that could be added, but it isn't there.
– Yakk - Adam Nevraumont
Nov 13 '18 at 0:06
add a comment |
Template argument deduction can't deduce anything that appears in a nested-name-specifier. For example, it won't allow T
to be deduced from a parameter of type typename Foo<T>::bar
. I believe that the reason for this is that such deduction is not possible in general; there can always be partial specializations of Foo
that define bar
to be some arbitrarily complicated typedef.
The workaround is to define the nested type as an unnested type then bring it in using a typedef
, but use the original unnested name for deduction:
template <unsigned i, typename... Ts>
struct A_B int b;
template <typename... Ts>
struct A
template <unsigned i> using B = A_B<i, Ts...>;
;
template <unsigned i, typename... Ts>
void foo(A_B<i, Ts...> var);
1
It is simply not reasonably to deduce type in such contest. For starters, it is not necessarily one-to-one mapping.
– SergeyA
Nov 12 '18 at 21:58
Thanks, I like this solution because it's simple
– tuket
Nov 12 '18 at 22:17
add a comment |
Template argument deduction can't deduce anything that appears in a nested-name-specifier. For example, it won't allow T
to be deduced from a parameter of type typename Foo<T>::bar
. I believe that the reason for this is that such deduction is not possible in general; there can always be partial specializations of Foo
that define bar
to be some arbitrarily complicated typedef.
The workaround is to define the nested type as an unnested type then bring it in using a typedef
, but use the original unnested name for deduction:
template <unsigned i, typename... Ts>
struct A_B int b;
template <typename... Ts>
struct A
template <unsigned i> using B = A_B<i, Ts...>;
;
template <unsigned i, typename... Ts>
void foo(A_B<i, Ts...> var);
1
It is simply not reasonably to deduce type in such contest. For starters, it is not necessarily one-to-one mapping.
– SergeyA
Nov 12 '18 at 21:58
Thanks, I like this solution because it's simple
– tuket
Nov 12 '18 at 22:17
add a comment |
Template argument deduction can't deduce anything that appears in a nested-name-specifier. For example, it won't allow T
to be deduced from a parameter of type typename Foo<T>::bar
. I believe that the reason for this is that such deduction is not possible in general; there can always be partial specializations of Foo
that define bar
to be some arbitrarily complicated typedef.
The workaround is to define the nested type as an unnested type then bring it in using a typedef
, but use the original unnested name for deduction:
template <unsigned i, typename... Ts>
struct A_B int b;
template <typename... Ts>
struct A
template <unsigned i> using B = A_B<i, Ts...>;
;
template <unsigned i, typename... Ts>
void foo(A_B<i, Ts...> var);
Template argument deduction can't deduce anything that appears in a nested-name-specifier. For example, it won't allow T
to be deduced from a parameter of type typename Foo<T>::bar
. I believe that the reason for this is that such deduction is not possible in general; there can always be partial specializations of Foo
that define bar
to be some arbitrarily complicated typedef.
The workaround is to define the nested type as an unnested type then bring it in using a typedef
, but use the original unnested name for deduction:
template <unsigned i, typename... Ts>
struct A_B int b;
template <typename... Ts>
struct A
template <unsigned i> using B = A_B<i, Ts...>;
;
template <unsigned i, typename... Ts>
void foo(A_B<i, Ts...> var);
answered Nov 12 '18 at 21:47
BrianBrian
64.3k795182
64.3k795182
1
It is simply not reasonably to deduce type in such contest. For starters, it is not necessarily one-to-one mapping.
– SergeyA
Nov 12 '18 at 21:58
Thanks, I like this solution because it's simple
– tuket
Nov 12 '18 at 22:17
add a comment |
1
It is simply not reasonably to deduce type in such contest. For starters, it is not necessarily one-to-one mapping.
– SergeyA
Nov 12 '18 at 21:58
Thanks, I like this solution because it's simple
– tuket
Nov 12 '18 at 22:17
1
1
It is simply not reasonably to deduce type in such contest. For starters, it is not necessarily one-to-one mapping.
– SergeyA
Nov 12 '18 at 21:58
It is simply not reasonably to deduce type in such contest. For starters, it is not necessarily one-to-one mapping.
– SergeyA
Nov 12 '18 at 21:58
Thanks, I like this solution because it's simple
– tuket
Nov 12 '18 at 22:17
Thanks, I like this solution because it's simple
– tuket
Nov 12 '18 at 22:17
add a comment |
In your specific case the nested type B
is uniquely attached to the specific specialization of the enclosing type A
. There's one-to-one correspondence between various B
s and their enclosing A
s. So, theoretically it should be possible to deduce the arguments of A
from a specific B
.
However, in general case the nested type name might refer to an alias (e.g. typedef-name), as opposed to a genuinely new type name (as in your example). In case of nested alias the 1:1 correspondence no longer holds, which is demonstrated by the examples in other answers.
The language specification does not distinguish between these two situations (a genuinely new nested type vs. a nested alias), so it opts for the "common denominator" approach and just always treats the enclosing template parameters as non-deduced context.
add a comment |
In your specific case the nested type B
is uniquely attached to the specific specialization of the enclosing type A
. There's one-to-one correspondence between various B
s and their enclosing A
s. So, theoretically it should be possible to deduce the arguments of A
from a specific B
.
However, in general case the nested type name might refer to an alias (e.g. typedef-name), as opposed to a genuinely new type name (as in your example). In case of nested alias the 1:1 correspondence no longer holds, which is demonstrated by the examples in other answers.
The language specification does not distinguish between these two situations (a genuinely new nested type vs. a nested alias), so it opts for the "common denominator" approach and just always treats the enclosing template parameters as non-deduced context.
add a comment |
In your specific case the nested type B
is uniquely attached to the specific specialization of the enclosing type A
. There's one-to-one correspondence between various B
s and their enclosing A
s. So, theoretically it should be possible to deduce the arguments of A
from a specific B
.
However, in general case the nested type name might refer to an alias (e.g. typedef-name), as opposed to a genuinely new type name (as in your example). In case of nested alias the 1:1 correspondence no longer holds, which is demonstrated by the examples in other answers.
The language specification does not distinguish between these two situations (a genuinely new nested type vs. a nested alias), so it opts for the "common denominator" approach and just always treats the enclosing template parameters as non-deduced context.
In your specific case the nested type B
is uniquely attached to the specific specialization of the enclosing type A
. There's one-to-one correspondence between various B
s and their enclosing A
s. So, theoretically it should be possible to deduce the arguments of A
from a specific B
.
However, in general case the nested type name might refer to an alias (e.g. typedef-name), as opposed to a genuinely new type name (as in your example). In case of nested alias the 1:1 correspondence no longer holds, which is demonstrated by the examples in other answers.
The language specification does not distinguish between these two situations (a genuinely new nested type vs. a nested alias), so it opts for the "common denominator" approach and just always treats the enclosing template parameters as non-deduced context.
answered Nov 12 '18 at 23:01
AnTAnT
258k32414657
258k32414657
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53270480%2fwhy-cant-the-template-parameters-of-this-function-be-inferred-for-this-nested-t%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown