Typedef,template和const关键字

时间:2015-05-30 12:08:45

标签: c++ templates const typedef

我在使用带有const关键字的模板时遇到问题(对于函数参数类型),为了说明我创建了一个很小的代码:

template <typename ClassType, typename ReturnType, typename ... Args>
struct MethodCallerFactory
{
    typedef ReturnType (*Type)(ClassType*, Args...);

    template <ReturnType (ClassType::*Pointer)(Args...)>
    struct Method
    {
        static Type createMethodCaller()
        {
            ReturnType (*caller)(ClassType*, Args...) = [](ClassType* obj, Args ... args) -> ReturnType
            {
                 ReturnType (ClassType::*ptr)(Args...) = Pointer;
                                return (obj->*ptr)(args...);
            };
            return caller;
        }
   };
};


class Test
{
public :
        void set(const int& p)
        {
                n = p;
        }
        void set2(int p)
        {
                n = p;
        }
        void set3(const int p)
        {
                n = p;
        }
        void set4(int& p)
        {
                n = p;
        }
private :
        int n;
};

typedef int& INTREF;
typedef int INTVALUE;

int main()
{
        void (*ptr1)(Test*, const int&) = MethodCallerFactory<Test, void, const int&>::Method<&Test::set>::createMethodCaller();
        void (*ptr2)(Test*, const INTREF) = MethodCallerFactory<Test, void, const INTREF>::Method<&Test::set>::createMethodCaller();

        void (*ptr3)(Test*, int) = MethodCallerFactory<Test, void, int>::Method<&Test::set2>::createMethodCaller();
        void (*ptr4)(Test*, INTVALUE) = MethodCallerFactory<Test, void, INTVALUE>::Method<&Test::set2>::createMethodCaller();

        void (*ptr5)(Test*, int) = MethodCallerFactory<Test, void, const int>::Method<&Test::set3>::createMethodCaller();
        void (*ptr6)(Test*, const INTVALUE) = MethodCallerFactory<Test, void, const INTVALUE>::Method<&Test::set3>::createMethodCaller();

        void (*ptr7)(Test*, int&) = MethodCallerFactory<Test, void, int&>::Method<&Test::set4>::createMethodCaller();
        void (*ptr8)(Test*, INTREF) = MethodCallerFactory<Test, void, INTREF>::Method<&Test::set4>::createMethodCaller();
}

我得到的唯一错误是第二个指针声明:

  

无法将模板参数'&amp; Test :: set'转换为'void(Test :: *)(int&amp;)'     void( ptr2)(Test ,const INTREF)= MethodCallerFactory :: Method&lt;&amp; Test :: set&gt; :: createMethodCaller();

似乎当我使用带引用类型的typedef时,编译器会忘记const关键字。我不明白为什么会有这个问题。

1 个答案:

答案 0 :(得分:6)

我认为纯文本替换会让您误以为const INTREF会解析为const int&(相当于int const&),但替换实际上会解析为int& const,其中{最终会忽略{1}}(这只是因为涉及const,因为您无法明确声明类型为typedef的变量)。所以你最终得到的是int& const而不是int&

在代码中:

int const&

Live example

这是为什么有些人(包括我)更喜欢采用#include <type_traits> typedef int& INTREF; int main() { static_assert(std::is_same<const INTREF, int&>{}, "!"); } - 之后风格超过const - 之前的风格(两种风格在技术上等同)的原因之一:除了更多一致, 1 它允许通过执行简单的文本替换来理解编译器正在做什么。如果你写了const而不是INTREF const,你可能会得出正确的结论。

1 您不能始终将const INTREF限定符放在它符合条件的类型之前,但您可以随时将它放在后面。例如,你可以拼出类型&#34;常量指针到一个常数整数&#34; as const(其中int const* const关键字始终适用于之前的所有内容),但语法不允许编写const;允许使用const const int*,但它会混合const int* const - 之前和const - 之后,从而降低一致性。