3

Consider the code below:

struct foo {
    int bar(int, int = 0) {
        return 0;
    }
};

constexpr auto ptr = &foo::bar;

int main() { 
    return (foo{}.*ptr)(0);
}

As expected, this code fails to compile with recent versions of GCC, Clang, and MSVC.

It is, however, plausible that a hypothetical compiler be capable of passing default arguments through aconstexpr member function pointer. If this compiler successfully compiled the code above <edit> without warnings </edit>, effectively passing 0, 0 to foo::bar, would it still conform to the ISO C++ standard?

An acceptable answer will reference the standard (or a working draft thereof). I have not yet found an answer in working draft N4567.

Edit: If the standard has no comment on this issue, I'll accept that as an answer as well.

Barrett Adair
  • 1,306
  • 10
  • 24
  • This question is a distillation of my previous [related question](http://stackoverflow.com/questions/35966588) (a poor one, in retrospect). – Barrett Adair Mar 14 '16 at 06:03
  • In "would it still conform", do you mean: would the compiler still conform ? – M.M Mar 14 '16 at 06:09
  • I don't see where it says that the default arguments should not be applied, even for the non-constexpr case – M.M Mar 14 '16 at 06:14
  • Yes, "would the compiler still conform" was my intended meaning. I left it ambiguous because I'm not sure, in the most pedantic sense, whether the onus of compliance is on the program or on the compiler. Practically speaking, I think the difference is meaningless -- but I digress. Sorry for the confusion. – Barrett Adair Mar 14 '16 at 06:18
  • Maybe I am missing something, but a function pointer can't have default arguments, as far as I know. – zdf Mar 14 '16 at 06:22
  • The standard does not explicitly disallow every invalid program or construct. Why do you think this one should merit a special mention? – n. m. could be an AI Mar 14 '16 at 06:22
  • As a rule, a compiler is allowed to accept invalid programs, as long as a diagnostic is issued. – n. m. could be an AI Mar 14 '16 at 06:25
  • @n.m. Because I don't know how to answer the question "Is the program above well-formed?" Other than to say, "Well, it doesn't work with this compiler, or that one..." – Barrett Adair Mar 14 '16 at 06:26
  • No, of course not. It is not well-formed. If you wanted to ask this question, why not just ask it, rather than asking an irrelevant question about hypothetical compilers? – n. m. could be an AI Mar 14 '16 at 06:28
  • @ZDF Indeed - the _type_ of the pointer is not really part of the question here. The `constexpr` value of what lies beneath the pointer is where I think the question begins. – Barrett Adair Mar 14 '16 at 06:29
  • I do not understand. Why do you think that adding constexpr would make a difference? A pointer is just an address; no additional information is stored. – zdf Mar 14 '16 at 06:33
  • `constexpr` doesn't mean "don't look at the type, look at the value". – n. m. could be an AI Mar 14 '16 at 06:42
  • @n.m. If it is not well formed as you say, feel free to post an answer explaining why. I also don't think it's fair to dismiss my question as irrelevant. It may not be relevant to you, but it may be relevant to a committee member, or to a compiler engineer. I'm not currently familar with Clang's or GCC's implementations of member pointers, but I imagine that it would be possible to add this functionality to one of them. – Barrett Adair Mar 14 '16 at 06:43
  • @ZDF `constexpr` is a much more plausible scenario, I think. Were it not `constexpr`, information about the default argument would need to be encoded in the pointer by some unconventional means. – Barrett Adair Mar 14 '16 at 06:45
  • constexpr would not make any difference: at compile time the compiler knows what function you are referring and therefore knows the default arguments. – zdf Mar 14 '16 at 07:05
  • The relevant question is whether the program is well-formed or not. A question about a hypothetical compiler makes little sense once you know the ansver to the well-formedness question. If you want the well-formedness question to be answered, ask it in the body of the post. – n. m. could be an AI Mar 14 '16 at 07:09
  • @n.m. The addition of `constexpr` to the language blurs the lines between runtime and compile time, and opens the language up to possibilities not previously considered. [De-facto?] limitations in place under C++98, such as the inability to apply default arguments through a pointer, are now up-for-grabs under `constexpr` rules. A compiler can now play fast-and-loose with `constexpr` indirection, and (continued...) – Barrett Adair Mar 14 '16 at 07:14
  • it's important to nail down the consequences of this in the standard. If GCC allowed the behavior in my example and Clang did not, fragmentation would creep into the C++ ecosystem. Since fragmentation runs counter to the purpose of a standard, it would be unfortunate for the standard to keep mum on this issue. This is a question about the standard, so I contend that the framing of my question is perfectly acceptable. – Barrett Adair Mar 14 '16 at 07:14
  • @ZDF No, the compiler would not necessarily know the function, because a pointer is generally a runtime construct. A mutable member function pointer is not bound to a single member function. Like any mutable pointer, it can be reseated. – Barrett Adair Mar 14 '16 at 07:17
  • 3
    Please reftain from secondary discussions. If you want to ask about well-formedness of program, please post a relevant question. It makes little sense to discuss what a conforming compiler is allowed to do. The answer is well-known. Let me reiterate, a conforming compiler is allowed to compile anything at all — an ill-formed C++ program, a Bash script, or a Mother Goose verse. It is, however, required to issue a diagnostic if it is anything else but a well-formed C++ program. Does this answer your question about behaviour of a hypothetical compiler? – n. m. could be an AI Mar 14 '16 at 07:28
  • Yes, runtime, but why would that stops it to push the right arguments? – zdf Mar 14 '16 at 07:30
  • @ZDF It would require runtime overhead to achieve what you describe. The generated code would need to check for the presence of a non-default argument at runtime, perhaps for every argument of every function pointer call in the program. I don't know whether the standard would allow this, but the payoff for such a scheme would be virtually zero. – Barrett Adair Mar 14 '16 at 08:39
  • You can have separate default arguments in different translation units. And the compiler cant know which member function is bound to a pointer. I don't know what constexprness has to do with it. – Piotr Skotnicki Mar 14 '16 at 08:42
  • @PiotrSkotnicki From the perspective of the standard, `constexpr` is irrelevant I suppose. From the perspective of the compiler, `constexpr` is relevant because `constexpr` (or `constexpr`-ish knowledge via [SSA](https://en.wikipedia.org/wiki/Static_single_assignment_form) or similar) lets the compiler know what lies underneath the pointer. When I wrote the question, I assumed the standard did in fact disallow application of default arguments through calls by [member] function pointer. Since the standard (apparently) says no such thing, perhaps `constexpr` should be removed from the title. – Barrett Adair Mar 14 '16 at 08:57
  • @PiotrSkotnicki Is the fact that default arguments can differ across translations units related to my question? I'm asking out of curiosity, not sarcasm - I couldn't tell whether that tidbit was addressed to ZDF. Couldn't the compiler simply use whatever defaults exist in the current translation unit's declarations? – Barrett Adair Mar 14 '16 at 09:15
  • I am at the point where I do not know if we are talking about the current standard or what would be possible. What I understand is that it would be nice to add the default parameters to the type signature. Once this done, constexpr or not, it would be possible, in certain circumstances, to use the default parameters. Since in current standard the type does not depend on defaults, we cannot talk about conformity. Again, I might miss something. – zdf Mar 14 '16 at 09:20
  • Maybe duplicate of [this question](http://stackoverflow.com/questions/2225330/member-function-pointers-with-default-arguments) ? (constexpr makes no difference) – M.M Mar 17 '16 at 06:33
  • @M.M. Yes, maybe so. Although most of the answer there is presupposed by this question, I think. – Barrett Adair Mar 17 '16 at 09:48

1 Answers1

6

The C++ standard does not explicitly disallow most ill-formed constructs, including this one. It is enough for it to not be explicitly or implicitly allowed to be ill-formed.

A conforming C++ implementation is allowed to accept anything at all, including ill-formed C++ programs. It is, however, require to issue at least one diagnostic if the input is not a well-formed C++ program (except in cases where an explicit "no diagnostic required" clause is present in the standard). So the question of what a conforming compiler is allowed to do rests on the question of whether the program in question is well-formed.

In order to address this latter question, we should establish whether constexpr bears any relevance. The answer to this is a definite "no". The constexpr keyword causes certain expressions that are otherwise not constant-expressions to become such, and has no other meaning. In turn, constant-expressions are different from plain vanilla expressions in a strictly limited number of well-defined contexts, such as array declarations or template instantiations. In such contexts a non-constant expression would render the program ill-formed. Since no such context is present in the program in question, we must conclude that constexpr bears no relevance on well-formedness of the program.

In particular, being a constant-expression does not free an expression from its obligation to be type-correct. As a trivial example,

`(int*)nullptr - (int*)nullptr`

is well-formed, while

`(int*)nullptr - (double*)nullptr`

is type-incorrect and thus ill-formed, despite both operands being constant expressions, known at compile-time to be equal to nullptr.

Disclaimer: C-style casts are for demonstration purpose only, as they read better in short code snippets. Don't use them in production code.

So we have to examine whether the program sans constexpr is well-formed or not. This question, surprisingly, doesn't have a direct answer in the standard, and may in fact have no answer at all. According to the statically-typed nature of C++, the call should be ill-formed, but I could not find a direct statement to this effect. Rather than pondering potential implications of this, I would rather declare this a technical defect of the standard and call it a day.

The standard does indirectly state that default function arguments are properties of function declarations rather than functions themselves:

(8.3.6/4) Declarations in different scopes have completely distinct sets of default arguments

Thus, a function pointer, being an entity that references a function rather than a function declaration, should not have default arguments applied.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • Excellent answer. In an earlier comment, I said "I'm not sure whether the onus of compliance is on the program or on the compiler. Practically speaking, I think the difference is meaningless." I see now that the difference is quite significant! I will remember this in the future - thanks. – Barrett Adair Mar 14 '16 at 09:40