|
Pretty dumb question. Is there less ugly/confusing than the below way to forward declare a class that is defined in a deeply nested namespace? For example, boost ASIO sockets:code:
|
# ? Jan 30, 2012 23:49 |
|
|
# ? Jun 5, 2024 04:14 |
|
Well you can do dumb things with macros that aren't at all worth it:code:
code:
|
# ? Jan 31, 2012 00:44 |
|
EnzoMafo posted:Pretty dumb question. Is there less ugly/confusing than the below way to forward declare a class that is defined in a deeply nested namespace? For example, boost ASIO sockets: No, but if you find yourself doing stuff like this a lot, you can make a macro for it: code:
That Turkey Story fucked around with this message at 00:52 on Jan 31, 2012 |
# ? Jan 31, 2012 00:49 |
|
code:
|
# ? Jan 31, 2012 05:10 |
|
A pointer?
|
# ? Jan 31, 2012 05:26 |
|
So that's basically option (a), then.
|
# ? Jan 31, 2012 05:28 |
|
Well if your use case is exactly the same as the psudocode you provided, you could do something like this:code:
Do any C++ compilers support delegating constructors yet? That would let you implement something like this while keeping the implementation details entirely hidden within the class, i.e.: code:
Paniolo fucked around with this message at 05:58 on Jan 31, 2012 |
# ? Jan 31, 2012 05:51 |
|
That Turkey Story posted:No, but if you find yourself doing stuff like this a lot, you can make a macro for it: Yeah, but your scientists were so preoccupied with whether they could that they didn't stop to think if they should.
|
# ? Jan 31, 2012 06:49 |
|
thermal posted:Yeah, but your scientists were so preoccupied with whether they could that they didn't stop to think if they should. I don't use the preprocessor so it's evil!!!
|
# ? Jan 31, 2012 06:58 |
|
That Turkey Story posted:I don't use the preprocessor so it's evil!!! not trying to be a luddite, just pointing out that nothing is gained by adding that code, and instead readability is lost. everyone knows what nested declarations look like; not everyone knows how the boost preprocessor library works. there is no value provided here, just obfuscation
|
# ? Jan 31, 2012 08:43 |
|
Wait, so TTS was serious?
|
# ? Jan 31, 2012 13:29 |
|
I'm always serious about C++.
|
# ? Jan 31, 2012 15:35 |
|
So, what am I doing wrong with this template function?code:
error: no matching function for call to 'conversion::convert_string(std::string&)' Whenever I try to run conversion::convert_string(somestring);
|
# ? Feb 2, 2012 03:43 |
|
You need to tell it what T is in this case, I believe.code:
Edit: By "isn't able to", I mean "doesn't". Never was quite sure what the reasoning for not doing it was though. Never bothered to look up the why in this case. chglcu fucked around with this message at 04:10 on Feb 2, 2012 |
# ? Feb 2, 2012 04:05 |
|
Princess Kakorin posted:So, what am I doing wrong with this template function? prolecat pointed out the problem. I don't know of the rationale for why, if there is one at all. If you really want the syntax you are looking for, you can get it by making convert_string not a template and having its definition just return an instance of a different type that holds the string argument by pointer or reference. That other type should have an overloaded conversion operator template that does what your current function template does.
|
# ? Feb 2, 2012 05:13 |
|
How could the compiler possibly know what T is supposed to be if you don't explicitly specify it and it's not a parameter?
|
# ? Feb 2, 2012 05:19 |
|
Paniolo posted:How could the compiler possibly know what T is supposed to be if you don't explicitly specify it and it's not a parameter? It can infer it from the type you are assigning to, in the same way it can use a templated conversion operator.
|
# ? Feb 2, 2012 05:28 |
|
Paniolo posted:How could the compiler possibly know what T is supposed to be if you don't explicitly specify it and it's not a parameter? It can deduce it just like an implicit conversion is deduced as long as the result is immediately used in a place that a specific type is expected.
|
# ? Feb 2, 2012 05:28 |
|
Thanks. It is pretty dumb it can't tell what type it needs to return. But whatever. Got it all working great now!
|
# ? Feb 2, 2012 05:32 |
|
It's easy to look at specific examples and think "the compiler should be able to infer that." The question is, how general do you want the facility to be, and what are you willing to sacrifice to make it possible? In this case, you have a call to a single function template whose template argument is used only in the result type, and it appears in a context which unambiguously states what that type should be. Is this the only case where this inference works?
I think the problem is mostly that it's really easy to come up with a rule that types exactly this case and falls over into exponential time or even undecidability with the slightest bit of extension. It's also a lot easier to understand overload resolution when you can actually extract out a specific bit of syntax and analyze it independently. The current rules are really damned complicated, but they actually work and extend reasonably well, and to do so they rely very heavily on being able to unambiguously assign a type to an expression without considering the context.
|
# ? Feb 2, 2012 06:34 |
|
That Turkey Story posted:It can deduce it just like an implicit conversion is deduced as long as the result is immediately used in a place that a specific type is expected. That's something very different, though. When a compiler sees an assignment operation it needs to know the type of the expression on the right-hand side so it can determine which overload of operator= (or a constructor) to invoke. When you're talking about implicit casts, the type of the right-hand side is already known, and you're just determining what intermediate operation is needed to transform that type into something which can be accepted by an existing overload of operator= (or a constructor.) Being able to do implicitly determine the type of an expression based on the context of an assignment higher up in the parse tree, even if you decided you would error out in any case that was slightly ambiguous, would require parsing C++ to be even more complicated than it already is, which is already too complicated.
|
# ? Feb 2, 2012 07:35 |
|
Paniolo posted:That's something very different, though. It's actually not different at all. Just replace "object with implicit conversion" with "object of unnamed type that has implicit conversion". The very fact that you can so easily emulate it in standard C++ in the manner I already described is proof of that. It does't have to do anything more complicated than that, which is why it works. If it were in the standard, I imagine it would even be described as such in terms of implicit conversion of an unnamed type. Paniolo posted:Being able to do implicitly determine the type of an expression based on the context of an assignment higher up in the parse tree, even if you decided you would error out in any case that was slightly ambiguous, would require parsing C++ to be even more complicated than it already is, which is already too complicated. Hmm? No changes to parsing should have to be done at all, this isn't a parsing problem. In terms of how the type is determined, just use the same rules as are used with implicit conversions. You don't have to look at the implementation of the function template or anything, just do the exact same matching as that of the return type of an implicit conversion. If after the deduction is done the corresponding function template has an error during instantiation then you get just that, an error during instantiation of the function template. Since you seem to be missing what I'm saying, here is exactly what I described in my earlier reply, trying to change as little from the earlier example as possible: code:
rjmccall posted:It's easy to look at specific examples and think "the compiler should be able to infer that." The question is, how general do you want the facility to be, and what are you willing to sacrifice to make it possible? Again, I don't see how this would make things any more complicated than the emulated version which already works in standard C++. If the coder wants that behavior he can get it with a little bit of boilerplate, so why not just let them do it directly? Also, the emulated version works in more cases than a specific type being needed -- specifically, it can do all of the matching that happens with the return type of any implicit conversion (such as a type with an implicit conversion to std::pair< T1, T2 >, where at least one of T1 and T2 are template parameters). That Turkey Story fucked around with this message at 10:13 on Feb 2, 2012 |
# ? Feb 2, 2012 09:49 |
|
It's not impossible or anything; Ada allows you to overload functions by return type. It might be useful sometimes but given how many opportunities there already are to shoot yourself in the foot with unexpected implicit conversions in c++ it's probably better not to have it.
|
# ? Feb 2, 2012 14:33 |
|
The1ManMoshPit posted:It's not impossible or anything; Ada allows you to overload functions by return type. It might be useful sometimes but given how many opportunities there already are to shoot yourself in the foot with unexpected implicit conversions in c++ it's probably better not to have it. I don't know, I think that's sort of a mistake in reasoning. We're emulating the functionality here via implicit conversions, since you want some similar behavior in terms of deduction, but it's not really an implicit conversion. For one, it's not implicit, at least not in the way that an implicit conversion is implicit -- you're making an explicit call to get the functionality, so it's not like you can accidentally run into the conversion when you don't want it. Second, you only use the result when it's deduced, otherwise you get an error (and you get an error if it's ambiguous), so it's not like you have an expression of a desired type and it's unexpectedly being converted to something else. You essentially have nothing at all before the deduction takes place. Think of the functionality more like the automatic deduction of template arguments, which is something that we take for granted already. When you call std::for_each, you probably don't think it's unsafe that the template arguments are deduced, right? In other words, you (thankfully) don't have to do: std::for_each< typename std::vector< T >::const_iterator, some_function_object >( my_T_vector.begin(), my_T_vector.end(), some_function_object() ); Instead, you just pass in the objects, leaving the template arguments to be deduced automatically by the compiler. If we lived in a world where you had to call function templates like the example I posted above, then it would be an error to not specify them when making a call. In the for_each example, what have we lost by automatically deducing the template arguments? In terms of functionality here, nothing. Arguably, someone could say that which instantiation is used may be unclear, though if that really were a problem then there's nothing stopping a programmer from being explicit even though they don't have to. In practice, we generally don't do that because the behavior is already pretty clear. A similar thing happens with automatic return type deduction. What happens currently in C++ when you try to do what the original poster was doing? You get an error. If we make it work, are we losing anything? Not really. Again, you can argue, just like with the template argument deduction example, that you are losing clarity, but if you really feel that that is the case, then there's nothing stopping you from being explicit if you want to, you just don't have to be explicit. And keep in mind that I'm not saying that the return type pattern should be considered when doing resolution for the function template being called. Even in that case, though, I'm not entirely convinced that it would be too horrible, only because I don't imagine that situations where it would be complicated to deduce would be common in practice.
|
# ? Feb 2, 2012 18:54 |
|
That Turkey Story posted:Again, I don't see how this would make things any more complicated than the emulated version which already works in standard C++. If the coder wants that behavior he can get it with a little bit of boilerplate, so why not just let them do it directly? Note that your emulation forces it down into exactly what I described as the easy case: the result type completely does not affect the choice of overload and the template arguments used in the "templated result type" are necessarily used only there. It also misbehaves in a ton of cases: argument-dependent lookup, template argument deduction, sizeof, decltype, etc. Basically, the only contexts in which it works are (1) calls/operators not involving templates and (2) initializations. The fact that the emulation does not affect overload resolution is very important; C++'s overload design relies on defining away function templates by applying template argument deduction as a separate phase, then (almost) completely ignoring whether a particular candidate is a template specialization. Trying to apply that faithfully here means that we have to simultaneously check an entire cluster of function calls and operators. Again, that's going to do evil things to ADL, which for operators really does matter; it's also going to foul up template argument deduction if any of the downstream calls are templated. Plus, as a minor matter, it's obviously combinatorial in time and space. If you *change* the rule, and maybe do overload resolution purely on call arguments against the inferred function signature, simply allowing template arguments used only in the result type to go unspecified for now, then we get back to something more workable, but we've also foreclosed a lot of potentially-interesting functionality. Ada does overloading by result type but (IIRC) does not have an equivalent of template argument deduction. rjmccall fucked around with this message at 19:48 on Feb 2, 2012 |
# ? Feb 2, 2012 19:45 |
|
rjmccall posted:Note that your emulation forces it down into exactly what I described as the easy case: the result type completely does not affect the choice of overload and the template arguments used in the "templated result type" are necessarily used only there. It also misbehaves in a ton of cases: argument-dependent lookup, template argument deduction, sizeof, decltype, etc. Basically, the only contexts in which it works are (1) calls/operators not involving templates and (2) initializations. I wouldn't call that misbehaving, I'd just call that a necessary limitation. Not being able to do sizeof( convert_from_string( "1234" ) ) is akin to trying to take the address of an overloaded function without explicitly casting for disambiguation. Just as the answer to taking the address of the overloaded function is "be explicit", so is the answer to wanting to do sizeof or decltype of the result of one of our function templates whose return type is to be deduced. Doing sizeof or decltype doesn't work because it's ambiguous. rjmccall posted:The fact that the emulation does not affect overload resolution is very important; C++'s overload design relies on defining away function templates by applying template argument deduction as a separate phase, then (almost) completely ignoring whether a particular candidate is a template specialization. Trying to apply that faithfully here means that we have to simultaneously check an entire cluster of function calls and operators. Again, that's going to do evil things to ADL, which for operators really does matter; it's also going to foul up template argument deduction if any of the downstream calls are templated. Plus, as a minor matter, it's obviously combinatorial in time and space. Again, I'm not advocating any of this. The emulated version is good enough for basically all cases and I wouldn't expect the language feature to go beyond that. A return type affecting overload resolution of the function template of which it is the return type is not at all what I originally suggested (that was a mouthful), nor is it what the original poster was looking for, anyway. rjmccall posted:Ada does overloading by result type but (IIRC) does not have an equivalent of template argument deduction. Right, I seem to recall reading about that in an interview with Stepanov where one of either he or Stroustrup wanted template argument deduction and the other was against it (or maybe it was in Elements of Programming). Either way, I'm grateful that we got it in C++.
|
# ? Feb 2, 2012 20:44 |
|
That Turkey Story posted:I don't know, I think that's sort of a mistake in reasoning. We're emulating the functionality here via implicit conversions, since you want some similar behavior in terms of deduction, but it's not really an implicit conversion. For one, it's not implicit, at least not in the way that an implicit conversion is implicit -- you're making an explicit call to get the functionality, so it's not like you can accidentally run into the conversion when you don't want it. Second, you only use the result when it's deduced, otherwise you get an error (and you get an error if it's ambiguous), so it's not like you have an expression of a desired type and it's unexpectedly being converted to something else. You essentially have nothing at all before the deduction takes place. I'm just not a big fan of implicit conversion in general, IMO the keyword "explicit" shouldn't even exist in c++, "implicit" should instead. The problem with allowing multiple return types is it becomes much harder to reason about which version of foo() is getting called when you assign to a type that requires an implicit conversion, much like it's already hard to reason about what overloaded function gets called. It becomes way harder when you have code like foo(bar()) where both foo & bar have multiple overloads. I don't think it's impossible and I have found myself thinking "gee it would be useful to be able to overload by return type here" before, but when I think about the implications due to c++'s already sometimes hard to understand function call resolution rules it makes me wary. quote:Think of the functionality more like the automatic deduction of template arguments, which is something that we take for granted already. When you call std::for_each, you probably don't think it's unsafe that the template arguments are deduced, right? In other words, you (thankfully) don't have to do: That kind of stuff can be cool, but here's a favourite counter-example of mine: which constructor gets called for i & which for j, and how many lines of code in the STL is required to make both of these calls do the same thing? code:
quote:A similar thing happens with automatic return type deduction. What happens currently in C++ when you try to do what the original poster was doing? You get an error. If we make it work, are we losing anything? Not really. Again, you can argue, just like with the template argument deduction example, that you are losing clarity, but if you really feel that that is the case, then there's nothing stopping you from being explicit if you want to, you just don't have to be explicit. Like I said, I think for sure there are places where it would be useful, but it's hard to try and wrap my brain around the number of crazy cases that would inevitably arise.
|
# ? Feb 3, 2012 15:21 |
|
The1ManMoshPit posted:IMO the keyword "explicit" shouldn't even exist in c++, "implicit" should instead.
|
# ? Feb 3, 2012 21:34 |
|
The1ManMoshPit posted:I'm just not a big fan of implicit conversion in general, IMO the keyword "explicit" shouldn't even exist in c++, "implicit" should instead. You'll get no argument from me there since I feel the same way. I'm also somewhat out of the ordinary in that I don't like the way automatically pulled-in default special member functions work. At the very least, I think automatic pulling-in of defaults should be inhibited in regard to the rule of 3 (I.E. if copy or assign or destruction are defined, inhibit the automatic pulling-in of defaults for the others). I'd much rather that you had to explicitly pull them in with something like C++11's = default syntax when you want them (or something less verbose). Anyway, I don't think the situation we are talking about here is entirely analogous to any of those things mentioned. I suppose if you really wanted to avoid that behavior, you could make it opt-in or opt-out by writing "implicit" or "explicit" when defining the template. Either way, I don't see anything wrong with the functionality itself. The1ManMoshPit posted:The problem with allowing multiple return types is it becomes much harder to reason about which version of foo() is getting called when you assign to a type that requires an implicit conversion, much like it's already hard to reason about what overloaded function gets called. It becomes way harder when you have code like foo(bar()) where both foo & bar have multiple overloads. In other words, if one of the bar overloads is a non-template, it will be preferred over the template in all cases. If there are just multiple bar overloads that are all templates and there are multiple matches that would be ambiguous without any regard to the return type, then the call is ambiguous and you get a compile error that could be resolved by being explicit. The fact that foo is overloaded should only affect things if bar() resolves to a function template with an automatically deduced return type and the overloads of foo have two different parameter types (or if it's dependent on a template parameter, etc.), in which case you get a compile error that could be resolved by being explicit. Second, your example is contrived. It's easy to come up with seemingly confusing situations when you use arbitrary functions that would likely not be designed in practice, especially when they are simply named foo and bar, giving us no information about what we would or should expect. So, taking your example, what are the functions foo and bar supposed to be doing here; why do these overloads exist/why were they designed in this manner; what would the person doing foo(bar()) reasonably expect to happen; does what he expects actually differ from what the compiler would do; if there is a difference, does the compiler produce an error, specifically one that could be fixed by being explicit, or does it compile fine but produce unexpected results at run-time? If you want to have a little more sway with me here, show me an example that could reasonably come up in code rather than one with just foo and bar. I could probably make any language feature look overly confusing with contrived code. Anyway, the main use-case is stuff like the situation that sparked this conversation. In practice, I can't imagine use-cases where you get horribly complicated situations. If you do, then IMO that's your mistake for using the feature in a confusing manner. If you have some code that helps your argument, I'd like to hear it, but right now I feel like what's generally being said is "it would be too confusing when a programmer uses it to write... confusing code". The1ManMoshPit posted:That kind of stuff can be cool, but here's a favourite counter-example of mine: which constructor gets called for i & which for j, and how many lines of code in the STL is required to make both of these calls do the same thing? I don't see how this really relates to what we are talking about. Anyway, if you want my personal opinion on the problem there, it's a combination of not having concepts, and IMO the interface could have been better designed. The1ManMoshPit posted:The fact that the STL is designed to hide the unexpected behaviour here is admirable, but it can place an unnecessary burden on developers wanting to create similarly robust code. There are obvious benefits when you finally have a working library, and if it's already made for you all the better, but there are a lot of crazy cases to account for if you want your code to be easy to call into. I imagine allowing overload by return type would just increase the number of workarounds of this type you would need to do.
|
# ? Feb 3, 2012 23:19 |
|
That Turkey Story posted:I don't see how this really relates to what we are talking about. Anyway, if you want my personal opinion on the problem there, it's a combination of not having concepts, and IMO the interface could have been better designed. It was just an example of unexpected behaviour with function overloading and the amount of effort some people have expended to make it work how a reasonable person would expect, rather than how strict typing dictates. I guess it's only related to overload by return type inasmuch as it deals with overloading in general. quote:I don't think that's true, at least not with how I've specified them. Show me how automatically deduced return types makes this worse. I have to admit when I tried to think of a not-totally-contrived example showing how overload by return value would make the situation worse I was stumped. I think I'm just getting old and conservative - I guess it's already annoying when you have to deal with the corner-cases of overload resolution and the thought of adding any more rules to it just generates a knee-jerk reaction. I mean, the number of contrived examples you can come up with is staggering, but as you say in practice no sane person would ever consider writing code that way. There would certainly be horrible examples of people writing bad code but that's really no different than what we have now.
|
# ? Feb 4, 2012 18:09 |
|
How does one clean up a factory generated object via unique_ptr? The logic I am looking for is as follows:code:
|
# ? Feb 4, 2012 18:15 |
|
This thread is massive and I have not read much of it, so I apologize if this has already been covered, but I'm kinda stuck. In my project I've got a list of both base and derived objects, like so: code:
code:
code:
I'm not really sure what to do here. The problem is that I will keep adding different derived classes and maintaining separate lists is just not a real solution. At different points in the project I may want to step through all base objects, or in other places I may want to process a few derived classes only. I just need some advice how to handle all these different kinds of objects and obviously prevent things like access violations in my crappy list setup that I have now. Thanks for your thoughts and ideas.
|
# ? Feb 5, 2012 13:15 |
|
The quick solution is to have a boolean variable "isDerived" that's on each object, and then filter non-derived objects out when you iterate through the entire list. Having said that, you'll get a much answer if you're specific with your details. Why do you want to iterate through base objects and don't care about derived objects? It seems like you're tying implementation details to the class hierarchy, which may violate the Liskov substitution principle.
|
# ? Feb 5, 2012 13:39 |
|
On a basic level, I'd do something like this:code:
The above is pretty inefficient, since each RemoveFromLists call triggers multiple linear searches through all your lists. If you can give each object a unique ID, you could use it as a key and store the objects in an std::map or std::hash_map instead, which would be much faster. You could also store the objects in std::set, using the pointer to the object itself as the key. The downside there would be the cost of adding new objects to the system would be higher, especially for map and set. Also, when you iterate through those containers, you won't get the objects in the order you inserted them, which I guess might be an issue for you. Edit: Suspicious Dish posted:Having said that, you'll get a much answer if you're specific with your details. Why do you want to iterate through base objects and don't care about derived objects? It seems like you're tying implementation details to the class hierarchy, which may violate the Liskov substitution principle. It's a fairly normal thing you may want to do in games. For example, you have your base entity type, and every frame you update all of them. However, some of those entities are bullets, which you want to do special collision checks with, so you keep a separate list of all the bullets to speed those checks up. Gerblyn fucked around with this message at 13:46 on Feb 5, 2012 |
# ? Feb 5, 2012 13:43 |
Gerblyn posted:You could also store the objects in std::set, using the pointer to the object itself as the key. If ordering is unimportant, use a set. Gerblyn posted:It's a fairly normal thing you may want to do in games. For example, you have your base entity type, and every frame you update all of them. However, some of those entities are bullets, which you want to do special collision checks with, so you keep a separate list of all the bullets to speed those checks up. You could also have only lists of the various derived objects (and maybe one of "miscellaneous other objects") and then have a virtual view of them all in one collection. I.e. write an iterator that returns base class pointers and actually iterates through several separate collections of various derived types, in order. (This has some fancy design pattern name that eludes me right now.)
|
|
# ? Feb 5, 2012 14:11 |
|
nielsm posted:A std::set should be pretty fast for both adding, removing and iterating. The downside is that it is unordered. It also does not allow duplicates which may be desirable or undesirable. Sets are ordered from least to greatest. There's also multi-set so that's not really a problem either.
|
# ? Feb 5, 2012 16:08 |
Captain Cappy posted:Sets are ordered from least to greatest. There's also multi-set so that's not really a problem either. Well you're right, although for pointers the less-than ordering is essentially arbitrary.
|
|
# ? Feb 5, 2012 16:44 |
|
You can use a custom comparison class though.
|
# ? Feb 5, 2012 16:50 |
|
Sigh... After implementing what Gerblyn suggested, I am still getting spurious crashes in times of high activity (during times when there are lots of objects being created and destroyed). Suspicious Dish did raise a good point. My derived classes have a lot of methods that the base class doesn't, and so I would have to rework my base class in order to correct that. I think I will rework the base class, and add a member function to the derived classes to return the class name. Then I can get away with a single list and still process objects only of a certain class when I want to. It's cheesy but my C++ skills are being stretched a lot trying to deal with this... ugh.
|
# ? Feb 5, 2012 17:55 |
|
|
# ? Jun 5, 2024 04:14 |
|
My Rhythmic Crotch posted:Suspicious Dish did raise a good point. My derived classes have a lot of methods that the base class doesn't, and so I would have to rework my base class in order to correct that. I think I will rework the base class, and add a member function to the derived classes to return the class name. Then I can get away with a single list and still process objects only of a certain class when I want to. For example: code:
|
# ? Feb 5, 2012 19:23 |