The code below is invalid C++, the line "Foo<int> b = a;" should fail to compile as implicitly casting is made illegal by the conditional explicit using the "IsSafelyCastable" predicate. ---------------------------------------------------------------- #include <type_traits> template <typename, typename> class IsSafelyCastable : public std::false_type {}; template <> class IsSafelyCastable<int, float> : public std::true_type {}; template <typename T> struct Foo { template <typename U> explicit(!IsSafelyCastable<T, U>::value) operator Foo<U>(); }; template <typename T> template <typename U> Foo<T>::operator Foo<U>() { return {}; } int main() { Foo<float> a; Foo<int> b = a; } ---------------------------------------------------------------- Clang 10 correctly evaluates the explicit condition to be true and blocks the implicit cast. However, GCC 9.3.0 successfully compiles without any errors. I believe this to be a GCC bug as subtle changes can make GCC produce the correct error. For example, moving the definition of the function to be inline with the declaration.
---------------------------------------------------------------- #include <type_traits> template <typename, typename> class IsSafelyCastable : public std::false_type {}; template <> class IsSafelyCastable<int, float> : public std::true_type {}; template <typename T> struct Foo { template <typename U> explicit(!IsSafelyCastable<T, U>::value) operator Foo<U>() { return {}; } }; int main() { Foo<float> a; Foo<int> b = a; } ---------------------------------------------------------------- Inlining the definition yields the correct error with GCC 9.3.0: ---------------------------------------------------------------- main.cpp: In function 'int main()': main.cpp:17:18: error: conversion from 'Foo<float>' to non-scalar type 'Foo<int>' requested 17 | Foo<int> b = a; | ----------------------------------------------------------------
Thanks for the report. In the first case we don't seem to ever substitute the explicit-specifier. I think we forget to lookup the explicit-specifier when instantiating the out-of-line definition of operator Foo. Since I've implemented explicit(bool), mine.
Reduced: template <typename T> struct Foo { template <typename U> explicit(static_cast<U>(true)) operator Foo<U>(); }; template <typename T> template <typename U> Foo<T>::operator Foo<U>() { return {}; } int main () { Foo<float> a; Foo<int> b = a; }
Patch posted: https://gcc.gnu.org/pipermail/gcc-patches/2020-May/545565.html
The master branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>: https://gcc.gnu.org/g:661232da72d29f8f2385d5f588727beb74360144 commit r11-371-g661232da72d29f8f2385d5f588727beb74360144 Author: Marek Polacek <polacek@redhat.com> Date: Mon May 11 18:28:19 2020 -0400 c++: explicit(bool) malfunction with dependent expression [PR95066] I forgot to set DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P when merging two function declarations and as a sad consequence, we never tsubsted the dependent explicit-specifier in tsubst_function_decl, leading to disregarding the explicit-specifier altogether, and wrongly accepting this test. PR c++/95066 * decl.c (duplicate_decls): Set DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P. * g++.dg/cpp2a/explicit16.C: New test.
The releases/gcc-10 branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>: https://gcc.gnu.org/g:ae275b986b8cc747a5b4f389cb05a71fdee1f886 commit r10-8280-gae275b986b8cc747a5b4f389cb05a71fdee1f886 Author: Marek Polacek <polacek@redhat.com> Date: Thu Jun 11 16:33:13 2020 -0400 c++: explicit(bool) malfunction with dependent expression [PR95066] I forgot to set DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P when merging two function declarations and as a sad consequence, we never tsubsted the dependent explicit-specifier in tsubst_function_decl, leading to disregarding the explicit-specifier altogether, and wrongly accepting this test. PR c++/95066 * decl.c (duplicate_decls): Set DECL_HAS_DEPENDENT_EXPLICIT_SPEC_P. * g++.dg/cpp2a/explicit16.C: New test.
Fixed.