You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++20中Concepts与CRTP结合的编译错误解决方法问询

问题:C++20 Concepts与CRTP结合的编译错误解决

在项目中尝试使用C++20的Concepts与CRTP模式,但在gcc-12和MSVC 2022环境下均出现编译错误。简化后的代码如下:

#include <iostream>
namespace Test
{
    namespace concepts {
        template<typename T>
        concept SupportsFn = requires(T t, int k) {
            {
                t.fn(k)
            };
        };
    }

    template<concepts::SupportsFn D>
    class CD {
        static_assert(concepts::SupportsFn<D>);
    public:
        void call(int k) {
            this->fn(k);
        }
    };

    template<concepts::SupportsFn T>
    class BD : public CD<T> {
        static_assert(concepts::SupportsFn<T>);
    public:
        void call2(int k) {
            this->fn(k + 2);
        }
    };
}

class ED : public Test::BD<ED> {
public:
    void fn(int k) {
        std::cout << "FN " << k << " \n";
    }
};

int main() {
    ED ed{};
    ed.call(10);
    return 0;
}

GCC和MSVC均提示因ED类型不完整导致模板约束不满足,MSVC错误信息如下:

C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.38.33130\include\expected(11): warning STL4038: The contents of are available only with C++23 or later.
1> C:\temp\memfnexpt\memfnexpt\memfnexpt.cpp(377,25): error C7602: 'Test::BD': the associated constraints are not satisfied
1> C:\temp\memfnexpt\memfnexpt\memfnexpt.cpp(368,2):
1> see declaration of 'Test::BD'
1> C:\temp\memfnexpt\memfnexpt\memfnexpt.cpp(367,11):
1> the concept 'Test::concepts::SupportsFn' evaluated to false
1> C:\temp\memfnexpt\memfnexpt\memfnexpt.cpp(353,5):
1> use of undefined type 'ED'
1> C:\temp\memfnexpt\memfnexpt\memfnexpt.cpp(377,7):
1> see declaration of 'ED'
1> C:\temp\memfnexpt\memfnexpt\memfnexpt.cpp(353,7):


解决方案

错误核心在于CRTP基类实例化时,派生类ED仍属于不完整类型——此时编译器还未读取到ED的fn成员定义,直接验证SupportsFn<ED>必然失败。以下是三种可行的解决方式:

方法1:修改概念为指针访问成员

调整SupportsFn概念,通过指针而非对象实例来检查成员函数。指针访问仅需要类的声明,不需要完整定义,适配CRTP的不完整类型场景:

#include <iostream>
namespace Test
{
    namespace concepts {
        // 改用指针检查成员函数,支持不完整类型
        template<typename T>
        concept SupportsFn = requires(T* t, int k) {
            { t->fn(k) };
        };
    }

    template<concepts::SupportsFn D>
    class CD {
        static_assert(concepts::SupportsFn<D>);
    public:
        void call(int k) {
            // CRTP推荐显式向下转型,避免依赖查找问题
            static_cast<D*>(this)->fn(k);
        }
    };

    template<concepts::SupportsFn T>
    class BD : public CD<T> {
        static_assert(concepts::SupportsFn<T>);
    public:
        void call2(int k) {
            static_cast<T*>(this)->fn(k + 2);
        }
    };
}

class ED : public Test::BD<ED> {
public:
    void fn(int k) {
        std::cout << "FN " << k << " \n";
    }
};

int main() {
    ED ed{};
    ed.call(10);
    ed.call2(10);
    return 0;
}

方法2:延迟约束检查到成员函数实例化

移除类模板的直接约束,将概念检查放在成员函数层面。此时约束会延迟到成员函数被调用时验证,而调用时派生类已完成定义:

#include <iostream>
namespace Test
{
    namespace concepts {
        template<typename T>
        concept SupportsFn = requires(T t, int k) {
            { t.fn(k) };
        };
    }

    // 类模板不直接加约束,转而在成员函数上声明
    template<typename D>
    class CD {
    public:
        void call(int k) requires concepts::SupportsFn<D> {
            static_cast<D*>(this)->fn(k);
        }
    };

    template<typename T>
    class BD : public CD<T> {
    public:
        void call2(int k) requires concepts::SupportsFn<T> {
            static_cast<T*>(this)->fn(k + 2);
        }
    };
}

class ED : public Test::BD<ED> {
public:
    void fn(int k) {
        std::cout << "FN " << k << " \n";
    }
};

int main() {
    ED ed{};
    ed.call(10); // 此时ED为完整类型,概念检查通过
    return 0;
}

方法3:两步继承+事后静态断言

提前声明派生类,基类不做约束,待派生类完整定义后用静态断言验证概念:

#include <iostream>
// 提前声明ED,让基类能识别该类型
class ED;

namespace Test
{
    namespace concepts {
        template<typename T>
        concept SupportsFn = requires(T t, int k) {
            { t.fn(k) };
        };
    }

    template<typename D>
    class CD {
    public:
        void call(int k) {
            static_cast<D*>(this)->fn(k);
        }
    };

    template<typename T>
    class BD : public CD<T> {
    public:
        void call2(int k) {
            static_cast<T*>(this)->fn(k + 2);
        }
    };
}

// 完整定义ED
class ED : public Test::BD<ED> {
public:
    void fn(int k) {
        std::cout << "FN " << k << " \n";
    }
};

// 事后验证ED满足概念要求
static_assert(Test::concepts::SupportsFn<ED>);

int main() {
    ED ed{};
    ed.call(10);
    return 0;
}

内容的提问来源于stack exchange,提问作者user1751716

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 15:58:19