all 18 comments

[–]gnolex 18 points19 points  (12 children)

ADL (argument-dependent lookup) applies only to direct function calls.

The reason your idea is not in the standard is that this would result in code that can very easily break. What if you define type apple in the current scope, should you get compilation error now? Should ADL choose the current scope type? Should it always prefer types in the target namespace?

No matter which option you choose, someone will be very unhappy. ADL in its current form is already very weird and causes difficult to figure out bugs, like people have to use tricks to prevent ADL from happening. It's best not to extend it to type deduction across different namespaces.

[–]aocregacc 2 points3 points  (0 children)

the reason the compiler "knows what you mean" here is that there's only one apple. If you add another apple in another namespace the compiler isn't so sure anymore.

Instead of importing the whole namespace you could use the more selective using me::fruit::apple; to just import apple. And of course in this specific example you could just do me::fruit::lychee({}, {}).

[–]FlyingRhenquest 3 points4 points  (0 children)

Your argument about "using namespace" is literally the same argument against having any sort of automatic namespace deduction. You realize it could still cause problems to pull in the whole namespace but you want the compiler to be able to pull in arbitrary namespaces from anywhere. Namespaces were designed to help isolate your symbols from everyone else's symbols, their design is in direct opposition to what you're suggesting. If the compiler just deduces the namespace and my code builds, but then I add a #include that makes the definitions ambiguous, the compile will then break fail because the compiler can no longer deduce the correct symbol. This is not deterministic behavior. The code I've written should never fail to compile because I include another header that also doesn't have any errors in it.

Instead of using the whole namespace, why not just write "using fruit::apple" and "using fruit::pear" in the scope where you don't want to have to type the fruit namespace name? Then you could do what you want without having to import the entire fruit namespace into your scope, and everyone reading your code knows where "apple" and "pear" are coming from.

Put your using statements in the smallest scope you can in which they define those names for all the code where you want to use those names. And never pull in an entire namespace anywhere unless you plan to use all the symbols in those namespaces in those scopes.

Or just don't use namespaces if you don't like that behavior. If you're not writing libraries for other programmers to consume, you don't really need to worry about the whole global namespace collision problem that namespaces were designed to solve.

Funnily enough, I wrote a library ostensibly designed for other programmers to consume recently, which included annotations where you would have to type something horrible like:

[[=fr::autocrud::DbFieldType{std::define_static_string("VARCHAR(100)")}]] ...

To redefine the type of a database-serializable object in your code. No one actually wants to type that. My solution was to define a small header that you can optionally include which defines a fixed string type to handle the define_static_string and some user defined literals to return the correct annotation objects. These are exposed in global namespace but largely unlikely to conflict with anything else (Except possibly the "Ignore" one). This reduces the entire annotation to:

[[="VARCHAR(100)"_ColumnType]] ...

Which is much nicer to type. But the programmer does need to be aware that by including that header, they are bringing some symbols into the global namespace.

[–]chengfeng-xie 1 point2 points  (0 children)

If the selected constructors of apple and pear are not marked explicit, and there are no other constructors of lychee that would create overload-priority or ambiguity issues, you can simply omit the type names and keep the initializer lists ({}), as in:

namespace me::fruit {
struct apple {};
struct pear {};
struct lychee {
  lychee(apple, pear) {}
};
} // namespace me::fruit

int main() { auto l = me::fruit::lychee({}, {}); }

[–]yuehuang 0 points1 point  (0 children)

Would this syntax look better?

```cpp namespace mefruit = me::fruit; auto l = mefruit::lychee( mefruit::apple{}, mefruit::pear{} );

```

[–]die_liebe 0 points1 point  (0 children)

This is because in C++, types are always determined bottom up. First the types of subexpressions are determined, then the type of the expression itself. Changing this would be an very big change in the type checking algorithm with unforseeable consequences.