简体   繁体   中英

C++20 template compilation passes

I am having a problem with the changes that were made to the way C++ templates are compiled, between the C++17 and 19 standards. Code that used to compile in VS2017 throws a compiler error since I upgraded to VS2019 or VS2022.

Situations have to do with the fact that the compiler now runs a basic syntax check on the template definition when it sees this definition ("first pass") and not only when the template is actually used.

Code example 1:

class Finder
{
    template<typename T>
    T convert_to(HANDLE h)
    {
        return Converters::Converter<T>::Convert(get_data(h));
    }
};

Here, the template class Converter<> resides in namespace Converters , and get_data is a member function of Finder which returns something that can be passed into the Convert function.

Since we're dealing with templates, this code sits in a header file "Finder.h". The header file doesn't #include "Converters.h" . Finder.h is shared across several projects, some of which don't even know the Converters.h file namespace.

As long as no code calls the MyClass::convert_to<> function, this compiles in VS2017, but not so in VS2019 and VS2022:

error C3861: 'Converters': identifier not found

The obvious solution is, of course, to #include "Converters.h" either in this header file, or in the precompiled headers file. However, as was said, Converters.h is not known in all places which use MyClass. Another solution would be to use archaic #define CONVERTERS_H in the Converters.h header and enclose the function definition in #ifdef CONVERTERS_H , but this looks really ugly.

My question is: Is there a way to prevent the compiler from doing this "first pass"? Or to re-write this code so that it compiles? I don't mind if it's MS specific; no other compiler will ever see the code.

Code example 2:

class MyClass2
{
    template<class T>
    static void DoSomething(T* ptr) { static_assert(false, "Don't do this"); }
    // lots more member functions, most of them 'static'
};
template<> void MyClass::DoSomething(CWnd* ptr) { /*some useful code*/ }
/// and some more specializations of DoSomething

The intention is that the static_assert should emit an error message whenever DoSomething is called with an argument for which no explicit specialization of this template function is defined. This worked in VS2017, but in VS2022, the "first pass" of the compiler triggers the static_assert .

Again, I wonder how I could achieve this effect, other than by replacing the static_assert by a run-time assertion.

Or am I thinking into a completely wrong direction?

Thanks

Hans

(partial answer)

To fix MyClass2 , the usual trick is to make false depend on T , so that the first pass does not trigger the assert.

// dependent false
template <typename>
constexpr bool dep_false() { return false; }

class MyClass2
{
    template<class T>
    static void DoSomething(T* ptr) {
       static_assert(dep_false<T>(), "Don't do this");
    }
    // lots more member functions, most of them 'static'
};

// specialization example
template<>
void MyClass2::DoSomething<int>(int* ptr) {
    std::cout << "int* is OK\n";
}

The first case requires a forward declaration of some kind, that's unavoidable.

The second case, though, can be handled with just a minor change.

#include <type_traits>

class CWnd {};

class MyClass2
{
public:
    template<class T, class Y=T>
    static void DoSomething(T* ptr) { static_assert(!std::is_same_v<Y,T>, "Don't do this"); }
};

template<> void MyClass2::DoSomething(CWnd* ptr) { /*some useful code*/ }

void foo()
{
    int a;
    CWnd b;

    MyClass2::DoSomething(&a); // ERROR
    MyClass2::DoSomething(&b); // OK
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM