I have got somewhat of a follow-up question to the scenario I described here, I found this post especially helpful for reasoning about my problem.
Background
In my original question, I was interested in overloading the std::make_shared
function to deal with partially specified template arguments:
std::make_shared<Container<1>::Internal>(arguments...);
Internal
and Container
are structs, which both possess one template argument, the one of Internal
is derived. (The full code is available below). The accepted solution works perfectly when specifying the template argument of Container
directly (1
in the line above).
Issue / Question
Then I tried to specify the template argument from a differentconstexpr
context:
std::make_shared<Container<A>::Internal>(arguments...);
where A
is a template argument of a third struct in which the above line is located.
Here I realised, that
- ... In the constructor, the line does not work and has to be changed into
std::make_shared<Container<A>::template Internal>(arguments...);
- ... In a member function the line works fine.
Question
Why is that the case?
Code
The code can be tried out here.
#include <memory>
#include <type_traits>
/// Container that is used in order to partially specify template arguments
template <int A> struct Container {
/// Contained type, of which the template arguments are deduced.
template <int B> struct Internal {
explicit Internal(std::integral_constant<int, B> fu) { (void)fu; }
};
};
/// Own make shared function / overload
namespace std {
template <template <int> typename partial, typename... Args> auto make_shared(Args &&... args) {
using target_type = decltype(partial{std::declval<Args>()...});
return std::make_shared<target_type>(std::forward<Args>(args)...);
}
} // namespace std
/// Some struct which uses its own template argument in order to create
template <int A> struct Producer {
template <typename... T> explicit inline Producer(T &&... t) noexcept {
auto const works = std::make_shared<Container<1>::Internal>(std::forward<T>(t)...);
// XXX: the following fails if "template" is omitted
auto const fails = std::make_shared<template Container<A>::Internal>(std::forward<T>(t)...);
}
template <typename... T> static auto test(T &&... t) noexcept {
/// XXX: Here, it works without specifying "template"
return std::make_shared<Container<A>::Internal>(std::forward<T>(t)...);
}
};
int main() { Producer<1>{std::integral_constant<int, 8>{}}; }
Compilation
- Compiler used:
g++-9.1
andg++-10.0
. - Compilation command:
g++ -std=c++2a sol.cc
Error
The following error message is generated when I omit 'template' in the constructor.
sol.cc: In instantiation of ‘Producer<A>::Producer(T&& ...) [with T = {std::integral_constant<int, 8>}; int A = 1]’:
sol.cc:34:58: required from here
sol.cc:25:29: error: dependent-name ‘Container<A>::Internal’ is parsed as a non-type, but instantiation yields a type
25 | auto const fails = std::make_shared<Container<A>::Internal>(std::forward<T>(t)...);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sol.cc:25:29: note: say ‘typename Container<A>::Internal’ if a type is meant
Own thoughts on the Issue
During name lookup
, the compiler determines whether the name denotes type / template or not.
If a name cannot be looked up until the actual template arguments are known it is a dependent-name
.
From what I understand that is the case here (at least in the constructor). Hence, in the constructor, the compiler is unable to determine that Internal denotes a template, whereas in the static member function, it is.
This might be the reason for Container<A>::Internal
being a dependent-name in the constructor, in case the type of the structure is to be automatically deduced by the compiler.
But in my use case I explicitly state it (Producer<1> in main()).
Note
I tried to provide a simpler example, but in that example this error did not occur, I think what I provided above is relatively minimal.