Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it an error to const-qualify a parameter pack of pointers?

When implementing a function which accepts a parameter pack of pointers to Ts..., why can't I const-qualify the pointers, as is possible with regular parameters?

I get a mismatching signature error on latest GCC and Clang, and I don't see why, since the pointers being const is just an implementation detail (hence it being legal for regular parameters).

template<typename... Ts>
class C
{
    void f(int*);
    void g(Ts*...);
};

template<typename... Ts>
void C<Ts...>::f(int* const) {} // Legal

template<typename... Ts>
void C<Ts...>::g(Ts* const...) {} // Compiler error

I am getting this error:

prog.cc:12:16: error: out-of-line definition of 'g' does not match any declaration in 'C<Ts...>'
void C<Ts...>::g(Ts* const...) {}
               ^
1 error generated.

You can also see the code and error here.

like image 644
Danra Avatar asked Mar 21 '18 09:03

Danra


People also ask

What effect does the const qualifier have when applied to a function argument?

Declaring the parameter 'const' adds semantic information to the parameter. They highlight what the original author of the code had intended and this will aid maintenance of the code as time goes by.

What effect does the const keyword have on a function parameter?

Declaring function parameters const indicates that the function promises not to change these values. In C, function arguments are passed by value rather than by reference. Although a function may change the values passed in, these changed values are discarded once the function returns.

What is const parameter?

A constant parameter, declared by the keyword const , is a read-only parameter. This means that we can not modify the value of the constant parameter in the function body. Using the const keyword tells the compiler that the value of that parameter will not be changed inside the function.


2 Answers

I'm going to chalk it up to a pair of compiler bugs (tested Clang and GCC). It's a bold assertion, I know, but [dcl.fct]/5 says, emphasis mine:

A single name can be used for several different functions in a single scope; this is function overloading. All declarations for a function shall agree exactly in both the return type and the parameter-type-list. The type of a function is determined using the following rules. The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”. After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list. [ Note: This transformation does not affect the types of the parameters. For example, int()(const int p, decltype(p)) and int()(int, const int) are identical types.  — end note ]

Which reads to me, quite clearly, that the declarations of both members (f and g) match the out-of-class definitions, making your program valid. So Clang and GCC should accept it.

like image 185
StoryTeller - Unslander Monica Avatar answered Sep 28 '22 23:09

StoryTeller - Unslander Monica


3 years later ... FYI ...

MSVC 19.28.29337 (with Visual Studio 2019 v16.8.5) compiles and runs the following with /W4 /Wx:

#include <iostream>

template<typename... Ts>
class C
{
public:
    void f(int*);
    void g(Ts*...);
};

template<typename... Ts>
void C<Ts...>::f(int* const pi) {
    using namespace std;
    cout << "f " << *pi << endl;
}

template<typename... Ts>
void C<Ts...>::g(Ts* const... tsargs) {
    using namespace std;
    cout << "g ";
    (cout << ... << *tsargs);
    cout << endl;
}

int main() {
    C<int, float, char> cifc;

    int* pi = new int{ 5 };
    cifc.f(pi);

    float* pf = new float{ 1.0f };
    char* pc = new char{ 'a' };
    cifc.g(pi, pf, pc);
}

But neither GCC HEAD 11.0.1 20210 nor Clang HEAD 13.0.0 does, with either C++17 nor C++2a.

Wandbox

like image 31
davidbak Avatar answered Sep 28 '22 23:09

davidbak