Consider the following piece of code:
template <typename T>
struct wrap {
T thing;
constexpr wrap(T thing) : thing(thing) {}
};
template <typename T>
wrap(const T&) -> wrap<T>;
template <wrap V>
void fun();
struct X {
int a;
};
int main() {
constexpr auto a1 = &X::a;
static const auto a2 = &X::a;
fun<a1>();
fun<a2>(); // Doesn't compile
}
Now the thing is that one can successfully pass a1 through wrap without having to state the template parameter explicitly, but not a2 which has static-storage duration.
If I change the above deduction guide to this:
template <typename T>
wrap(const T&) -> wrap<const T&>; // <-- Note the 'const T&' here instead of plain 'T'
Then one is able to pass a2 but not a1.
If possible, how to modify the above code so that one is able to pass both a1 and a2 respectively without having to explicitly state the types like fun<wrap<decltype(a1)>{a1}>() or fun<wrap<const decltype(a2)&>{a2}>()?
Sure, it's possible. However, before I explain the solution, please allow me to suggest that you have made the problem unnecessarily difficult by insisting on a particular interface which is not (in my opinion) actually cleaner than the alternatives. Essentially, you are asking for
fun<arg>to either takeargby value or by reference, depending on which one is actually well-formed. In the case ofa1, it may only be taken by value; it cannot be taken by reference because it doesn't have static storage duration. In the case ofa2, it cannot be taken by value because it wasn't declaredconstexpr, but can be taken by reference because it has static storage duration.The code using your proposed version of
funis difficult to read, because the reader, seeingfun<arg>, does not immediately know whetherargis being taken by value or by reference. The reader must infer which one it is, based on the reader's own knowledge of whetherargis a permitted non-type template parameter by value or by reference. Furthermore, some arguments may qualify as either, and in that case the reader would also have to know which default the implementer offunhas selected for that case, in order to know what is going on.Again, this is my opinion only: it would be much simpler if you wrote separate functions, perhaps calling them
fun_valandfun_ref, wherefun_val<a1>()andfun_ref<a2>()are well-formed. For this, we should define two wrapper classes, one which takes the argument by value, and one by reference:Now, if you insist on having a single name
fun, then the key is to recognize thata1anda2have the same type, so a single application of CTAD will never be able to figure out the correct wrapper type to make the invocation well-formed. Instead, you have to use SFINAE with two overloads: the one that is invalid for the given template argument (because it takes the argument by value (resp. reference) that cannot be taken by value (resp. reference)) is discarded. Basically, rename bothfun_valandfun_refin the above example to simplyfun:This works fine in the case of
a1anda2, for which only one of the two overloads is a candidate. But in the case ofx, it will be ambiguous. Let's say you want to force the by-value overload to be selected in that case. We can do that by inserting a constraint that makes the by-reference overload not a candidate:You can view the full working example here.