[libcxx-dev] std::variant<> and overload resolution

Arthur O'Dwyer via libcxx-dev libcxx-dev at lists.llvm.org
Wed Aug 25 09:05:25 PDT 2021

On Wed, Aug 25, 2021 at 9:04 AM Brian Cain via libcxx-dev <
libcxx-dev at lists.llvm.org> wrote:

> For the test case below, clang++-12 does not emit an error (likely the
> same on ToT).  Nor does g++-9.4.0 with libstdc++.  But should they?

No. Why do you wonder if they should?
std::variant construction works by "fictitious overload set": it goes
basically as if you'd written
    void f(int);
    void f(S<A>);
    ... f(1); ...

In the fictitious overload resolution, the `int` argument is a better match
for `f(int)` than for `f(S<A>)`, so `f(int)` is chosen. Back in the real
world, that means that the variant `b` is initialized via its `int`
alternative, not its `S<A>` alternative.
However, this does turn out to get weird when it comes to narrowing
conversions, and I haven't investigated the wording to find out why.
    std::variant<int, S<A>> b(int(1));  // initializes the `int`, duh
    std::variant<int, S<A>> b(short(1));  // initializes the `int`
    std::variant<int, S<A>> b(char(1));  // initializes the `int`
    std::variant<int, S<A>> b(long(1));  // initializes the `S<A>` !?!?!
even though a call to `f(long(1))` would resolve to `f(int)`, not `f(S<A>)`.

Finally, btw, note that you wrote `S(T&&...)` instead of `S(T&&)`.
Surprisingly, this is valid C++ syntax, because a trailing `...` can mean
variadic-function ellipsis instead of parameter-pack-expansion; it's
exactly as if you wrote
    S(T&&, ...) {}
Since you're passing only one argument anyway, the trailing ellipsis
matches no arguments, contributes nothing to overload resolution, and
therefore doesn't matter at all.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/libcxx-dev/attachments/20210825/3fc8c1ab/attachment.html>

More information about the libcxx-dev mailing list